Compare commits

...

730 Commits
2.3 ... 2.4.0

Author SHA1 Message Date
FrancescoUK
54455213a8 Release files update 2016-04-02 09:33:37 +01:00
FrancescoUK
dda468291a Update codeigniter_version.txt to contain 3.0.5 (#292)
change the order of access prevent in .htaccess
2016-04-01 22:17:13 +01:00
jekkos
bef7a2da25 Add release notes for 2.4 2016-04-01 18:43:05 +02:00
jekkos
953307cbc7 Remove F-key shortcuts for modules (#425) 2016-03-31 08:08:12 +02:00
jekkos
4d04cdd65f Fix error message when searching for nonexistant supplier in receivings
(#430)
2016-03-29 17:41:16 +02:00
jekkos
51b776354a Fix error message when searching for nonexistant customer in sales
(#430)
2016-03-29 08:43:58 +02:00
jekkos
d51d1b458a Regenerate invoice number on sale unsuspend if no custom format is used
(#423)
2016-03-29 08:29:43 +02:00
FrancescoUK
faac1d6a10 fix bulk_edit: don't remove category, supplier and taxes if left empty; save when a value is set (#393 #381) 2016-03-26 15:12:51 +00:00
jekkos
6ffec444d6 show serialnumber and description on invoice underneath name and qty
resp. (#405)
2016-03-23 18:44:15 +01:00
jekkos
4f3a845702 Prevent user to update employee without selecting at least one grant
(#420)
2016-03-23 08:38:13 +01:00
jekkos
a3af051d43 Minor improvements for sales tax bulk edit (#381) 2016-03-23 08:14:15 +01:00
jekkos
79a4414a13 Disable second tax field if first is not filled (#381)
Add extra warning if first tax is modified
2016-03-22 08:55:43 +01:00
jekkos
98ad6f3db0 Ignore giftcard numbering test as it makes the build unstable (#114) 2016-03-21 21:51:59 +01:00
jekkos
258c95f02e Remove sync ajax calls in validation callback for forms (#413)
Remove jquery-bgiframe (not used anynmore) (#328)
Fix bug in receiving form save if supplier was empty

Conflicts:

	application/controllers/Items.php
	application/views/customers/form.php
	application/views/items/form.php
	application/views/partial/header.php
	application/views/receivings/form.php
	application/views/sales/form.php
	bower.json
	dist/opensourcepos.js
	dist/opensourcepos.min.js
	templates/spacelab/views/partial/header.php
2016-03-21 21:43:52 +01:00
FrancescoUK
e6472d525d add rule to .htaccess to protect generate_language.php (#311) 2016-03-21 19:26:31 +00:00
jekkos
07e372a787 Properly fix for item search bug (remove duplicated rows due to left
outer join)
2016-03-21 17:43:59 +01:00
FrancescoUK
b9c088314b fix is_deleted issue on some installation (#406) 2016-03-21 16:36:10 +00:00
jekkos
611237ec86 Fix search resultset when no filters applied 2016-03-21 08:39:24 +01:00
jekkos
b5e07c9245 Move print_hide to outer div (#394) 2016-03-18 19:02:10 +01:00
jekkos
c6c6a1b4cf Fix print button for receipt and invoice (#394) 2016-03-18 19:00:08 +01:00
jekkos
8cdb30fd25 Show item name on invoice if serialized and description empty (#405) 2016-03-18 18:43:23 +01:00
FrancescoUK
91041bd7a9 Add navigation buttons to invoice, receipt and takings + enable jsPrint (#394) 2016-03-18 18:43:07 +01:00
jekkos
bc53f544f4 Replace custom csv parse function with PHP builtin (>=5.3) (#311) 2016-03-18 14:12:46 +01:00
jekkos
d29176158b Strip eol characters at end of last column (#311) 2016-03-18 08:16:52 +01:00
jekkos
8fc24219fb Strip eol characters from imported csv line (#311) 2016-03-17 12:20:28 +01:00
jekkos
09dd7eca58 Make invoice header responsive, remove fixed height and make font size
relative. (#116)
2016-03-15 17:52:43 +01:00
FrancescoUK
7937e880c0 Merge branch 'ais-one-master' 2016-03-13 15:30:40 +00:00
FrancescoUK
c52b5b5118 hide links, change print button id and add it to stylesheet, hide the print div (#160) 2016-03-13 15:29:39 +00:00
FrancescoUK
e47e80c69c Merge branch 'master' of git://github.com/ais-one/opensourcepos into ais-one-master 2016-03-13 14:39:37 +00:00
Aaron Gong
6208569c7e Fix for issue 160
https://github.com/jekkos/opensourcepos/issues/160

I try to interfere as little as possible
2016-03-13 22:04:39 +08:00
FrancescoUK
4ad0d62f42 Upgrade to CI3.0.5 (#292) 2016-03-13 11:26:51 +00:00
FrancescoUK
0bd5bbec77 remove encode/decode base64 causing issues with web hosting providers (#374) 2016-03-13 10:52:32 +00:00
jekkos
d510ab031d Update README.md with FAQ and answer to common setup problem (#387, #97, 2016-03-10 08:43:07 +01:00
jekkos
9c39f62b1e Fix sale search form submission on enter (#386)
Add php5-apcu to Dockerfile (#320, 284)
Fix countrycode parameter for nominatim address lookup
2016-03-10 08:25:32 +01:00
FrancescoUK
bf2c6bfa1f fix issue with sale edit 2016-03-08 21:53:29 +00:00
FrancescoUK
5588c1e22e set a ospos_session cookie name 2016-03-08 21:30:25 +00:00
FrancescoUK
525fb7dcf9 fix issue blocking searching suggestion 2016-03-08 21:28:41 +00:00
FrancescoUK
e182c9d9f6 regenerate dist files 2016-03-08 21:23:05 +00:00
FrancescoUK
76639f6da1 remove misleading comment from code 2016-03-08 19:50:46 +00:00
FrancescoUK
d7df291b9a Merge branch develop/2.4 into master 2016-03-08 19:14:36 +00:00
FrancescoUK
1a1266a20c touched version to be 2.4.0, updated README.md, enabled Cross Site Request Forgery in config.php 2016-03-08 18:19:28 +00:00
jekkos
f366757476 Add group operator to giftcard query 2016-03-08 18:18:27 +00:00
jekkos
741843586d Remove duplicated cookie cleanup hack (CI 2.x issue) 2016-03-08 18:18:27 +00:00
FrancescoUK
fafdfd9afd fix php error on suggestion typing when in sale register return mode (#331) 2016-03-08 18:18:27 +00:00
FrancescoUK
a156ad8087 Fix issue in Sales/Takings and apply same fix to other controller to avoid similar issues in the future (#337) 2016-03-08 18:16:31 +00:00
FrancescoUK
b36a03c4bc Fix to sales edit server error when customer is '' (#337) 2016-03-08 18:16:31 +00:00
jekkos
028bdb074b Add caching hints for mod_expires (#340) 2016-03-08 18:16:31 +00:00
jekkos
643ceb25b5 Fix spacing in autocomplete suggestion 2016-03-08 18:16:31 +00:00
jekkos
5f8eface28 Properly save company_name in customer form 2016-03-08 18:16:31 +00:00
jekkos
8a5177f14c Fix base url for testcases (#305)
Fix legacy ubuntu repository urls (#284)
2016-03-08 18:16:31 +00:00
jekkos
02c8a89bbe Use curl to wait for apache start (#305) 2016-03-08 18:13:24 +00:00
jekkos
c67acaa6ec Make 2.4 version string npm compatible 2016-03-08 18:04:15 +00:00
FrancescoUK
4bbacf6923 Code reformatting 2016-03-08 18:04:15 +00:00
FrancescoUK
38ea682c88 Add favicon 2016-03-08 18:00:33 +00:00
FrancescoUK
0f1208d4d9 Fix package.json version to be 2.4 (#284) 2016-03-08 17:51:16 +00:00
jekkos
69942283e5 Make docker work with 2.4 branch 2016-03-08 17:50:17 +00:00
FrancescoUK
d13e034940 Update database/tables.sql script with new sessions table (#120) 2016-03-08 17:44:59 +00:00
FrancescoUK
72059184df Update Copyright notice (#120) 2016-03-08 17:44:59 +00:00
FrancescoUK
4d9dc0111e Make the data blob NOT NULL in the SQL upgrade script (#120) 2016-03-08 17:44:59 +00:00
FrancescoUK
b9262230d1 Change sessions to be stored in database and add SQL upgrade script 2.3.4 to 2.4 (#120) 2016-03-08 17:44:59 +00:00
FrancescoUK
df4cea3228 Fix to custom fields that cannot be null on Items save (#120) 2016-03-08 17:44:59 +00:00
jekkos
0f27348924 Add (temp) fix for cur_page conuter in CI pagination libary (#284)
Fix .htaccess file
2016-03-08 17:44:59 +00:00
FrancescoUK
dd5aa1e241 Restore padding in pagination links, now it uses css (#120) 2016-03-08 17:44:59 +00:00
FrancescoUK
5df62610d2 Upgrade to CI 3.0.4 (#120) 2016-03-08 17:44:59 +00:00
FrancescoUK
9dcbef988a Added comments to some controller functions and improved spacing to improve readibility of code (#120) 2016-03-08 17:44:59 +00:00
FrancescoUK
d1a973564f Make NULL to be null all over the code for consistency purposes (#120) 2016-03-08 17:44:59 +00:00
FrancescoUK
da52ff99c1 Further review and fixes to input->post(...) (#120) 2016-03-08 17:44:59 +00:00
FrancescoUK
ee07676193 Fix to items search not working due to input->post returning null (#120) 2016-03-08 17:44:59 +00:00
FrancescoUK
007598df6a Fix merge miss in constants.php (#120) 2016-03-08 17:44:59 +00:00
FrancescoUK
9a71bbc620 Complete CI 3.0.3 upgrade since it wasn't done in full (#120) 2016-03-08 17:44:59 +00:00
FrancescoUK
a5041a3a41 Remove application/errors, upgrade Step 8 (#120) 2016-03-08 17:44:59 +00:00
FrancescoUK
36d3717bd6 EXT constant deprecated, upgrade Step 20 (#120) 2016-03-08 17:44:59 +00:00
FrancescoUK
abd63731be Follow upgrade guideline Step 9 (#120) 2016-03-08 17:44:59 +00:00
FrancescoUK
b7c0e32cd0 Fix reports error due to report.php now being Report.php (#120) 2016-03-08 17:44:59 +00:00
FrancescoUK
c512d7167d Change config language to be 'en' consistent with the OSPOS language identifiers (#120)
However language support doesn't work properly at the moment.
2016-03-08 17:44:59 +00:00
FrancescoUK
6ccfc8ba7d Improve .htaccess with some security rules (#120) 2016-03-08 17:44:59 +00:00
FrancescoUK
b27d3c04c9 Upgrade config.php and database.php.tmpl to CI 3.0.3 versions (#120)
Apparently the upgrade wasn't complete.
2016-03-08 17:44:59 +00:00
FrancescoUK
aa3948b9a2 Remove application/config.php and database.php (#120) 2016-03-08 17:41:16 +00:00
Toni Haryanto
75fa1a8167 Upgrade to Code Igniter 3.0.3 (#120) 2016-03-08 17:41:16 +00:00
jekkos
9cd42e0614 Add group operator to giftcard query 2016-03-08 08:12:23 +01:00
Aaron Gong
ff63adecdc Fix For #339
https://github.com/jekkos/opensourcepos/issues/339

We should also rollout the fixes to  develop/2.4 if necessary. This fix
was adapted from BootstrapUI which fixed the PHP 7 error regarding
classes.
2016-03-07 22:26:07 +08:00
jekkos
f0af6f8927 Correct spanish translations for reorder level warning (#365) 2016-03-06 13:39:11 +01:00
jekkos
a201d18938 Add specific warning when stock goes below reorder level in sales (#365, #336) 2016-03-02 08:28:50 +01:00
jekkos
d4f456d868 Bump version strings to 2.3.5 2016-02-29 22:36:25 +01:00
FrancescoUK
af2736e462 Reverse PHP7 fix causing issues with DB functionality (#351) 2016-02-27 10:15:58 +00:00
jekkos
3cb9aaa055 Remove duplicated cookie cleanup hack (CI 2.x issue) 2016-02-25 23:28:19 +01:00
FrancescoUK
831dc7a0b8 fix php error on suggestion typing when in sale register return mode (#331) 2016-02-25 15:44:13 +00:00
FrancescoUK
03146d9e3b Fix issue in Sales/Takings and apply same fix to other controller to avoid similar issues in the future (#337) 2016-02-22 17:52:29 +00:00
FrancescoUK
45c0163a9d Fix to sales edit server error when customer is '' (#337) 2016-02-22 08:51:18 +00:00
jekkos
5f0028a983 Add caching hints for mod_expires (#340) 2016-02-20 21:40:06 +01:00
jekkos
f9c882bf24 Fix spacing in autocomplete suggestion 2016-02-08 17:01:37 +01:00
jekkos
7180e7e9ed Update changelog for 2.3.4 2016-02-08 07:42:04 +01:00
FrancescoUK
445222bcdd Merge branch 'master' into ci3 2016-02-07 18:03:44 +00:00
jekkos
40ddfdc2ab Merge pull request #319 from daN4cat/master
Remove dbbackup on logout #318
2016-02-07 15:09:02 +01:00
FrancescoUK
345d2d95d8 Remove dbbackup on logout (#318) 2016-02-05 18:42:04 +00:00
FrancescoUK
8d47faadd7 Merge branch 'master' into ci3
# Conflicts:
#	package.json
2016-02-05 17:01:07 +00:00
FrancescoUK
d07ed0c09f Merge remote-tracking branch 'jekkos/develop/2.4' into ci3 2016-02-05 16:57:27 +00:00
jekkos
24b4c98d94 Merge pull request #317 from daN4cat/master
Fixes to phppos to ospos 2.3.4 migration script #258
2016-02-05 17:36:16 +01:00
FrancescoUK
ea6fffc525 Add version liner so we remember which version the script works with (#258) 2016-02-05 09:06:54 +00:00
FrancescoUK
a0b422be34 Fix source script phppos_migrate.sql too (#258) 2016-02-05 09:05:57 +00:00
FrancescoUK
6599ea6678 Fix and add data copy rules. Tested script (#258) 2016-02-04 23:04:13 +00:00
jekkos
36cf19b1b9 Properly save company_name in customer form 2016-02-04 23:09:03 +01:00
FrancescoUK
9d056b2765 Remove invoice_number from phppos SQL migration script (#258) 2016-02-03 18:14:00 +00:00
jekkos
5c16c2a21a Use grep and wait for mysql to come up properly (#305) 2016-02-03 17:42:44 +01:00
jekkos
acd9beae73 Set npm package version number 2016-02-03 17:29:01 +01:00
jekkos
e598817287 Set correct version for npm package 2016-02-03 17:18:28 +01:00
jekkos
35d53e2fae Fix base url for testcases (#305)
Fix legacy ubuntu repository urls (#284)
2016-02-03 11:09:45 +01:00
jekkos
fa3d77299e Use curl to wait for apache start (#305) 2016-02-03 11:04:09 +01:00
jekkos
48a16d65e1 Fix base url for testcases (#305)
Fix legacy ubuntu repository urls (#284)
2016-02-03 10:49:20 +01:00
jekkos
89f126ab04 Use curl to wait for apache start (#305) 2016-01-31 16:03:19 +01:00
jekkos
67d9ca1e41 Merge branch 'master' into develop/2.4
Conflicts:
	Dockerfile
2016-01-29 17:34:53 +01:00
jekkos
8f9d80a809 Wait for container before running integration tests (#305)
Add integratoin test for quantity units (#305)
2016-01-29 17:24:28 +01:00
FrancescoUK
3425920cb6 Merge branch 'master' into ci3 2016-01-28 09:05:35 +00:00
jekkos
85c6da4346 Add integration test for making receiving (#305)
Update docker scripts to work with data volumes (#284)
2016-01-27 20:11:21 +01:00
jekkos
93460daadf Make 2.4 version string npm compatible 2016-01-27 11:00:18 +01:00
jekkos
574548f360 Merge branch 'master' of https://github.com/jekkos/opensourcepos 2016-01-26 20:27:58 +01:00
jekkos
847c4a67c9 Add integration test for making sale (#305) 2016-01-26 20:27:07 +01:00
jekkos
8aab154a9f Disable sudo for travis tests, omit nodejs version 2016-01-26 16:27:06 +01:00
jekkos
a3119d1993 Add integration test for creating new item 2016-01-26 08:44:48 +01:00
jekkos
0a159c803f Add testcase for creating new item
Optimise CI setup
2016-01-25 21:14:48 +01:00
jekkos
22a7231d8f Run docker container in background + launch tests once ready 2016-01-25 19:16:13 +01:00
jekkos
c36f6b603d Run docker interactively in travis script step 2016-01-25 17:53:47 +01:00
FrancescoUK
8e5b8712c0 Merge branch 'master' into ci3 2016-01-25 09:20:04 +00:00
jekkos
f063aa16a8 Launch webdriver test in docker container 2016-01-25 09:34:10 +01:00
jekkos
2520970698 Fix comment for autogenerated sql files (#301, #258) 2016-01-25 08:42:15 +01:00
jekkos
a21a825aac Merge pull request #299 from daN4cat/master
Add a live clock functionality (#298) and fix Gruntfile.js (#297)
2016-01-25 08:36:43 +01:00
FrancescoUK
d48dc8b87b Merge branch 'master' into ci3 2016-01-24 22:44:45 +00:00
FrancescoUK
8f72756737 Add a live clock functionality (#298) fix Gruntfile.js (#297) 2016-01-24 22:34:05 +00:00
FrancescoUK
68b28a17e4 Merge branch 'master' into ci3 2016-01-24 10:38:26 +00:00
jekkos
463af45e73 Remove test run in pre_install (travis-ci) 2016-01-24 10:28:00 +00:00
FrancescoUK
4ccab6b8b5 Code reformatting (#296) 2016-01-24 10:27:41 +00:00
FrancescoUK
1849ee807c Add favicon (#296) 2016-01-24 10:27:15 +00:00
FrancescoUK
4dd3c64e9d Code reformatting 2016-01-22 22:16:50 +00:00
FrancescoUK
b400393a90 Code reformatting 2016-01-22 22:14:42 +00:00
FrancescoUK
1f9cb92697 Add favicon 2016-01-22 19:14:57 +00:00
FrancescoUK
59fe366479 Add favicon 2016-01-22 19:05:07 +00:00
FrancescoUK
8eba67ceb8 Merge branch 'master' into ci3 (#284) 2016-01-22 14:57:25 +00:00
jekkos
1148ef1595 Install grunt-cli in docker container (#284) 2016-01-22 14:43:53 +01:00
jekkos
bcbdee3a50 Fix test execution for travis-ci (grunt invocation) 2016-01-22 13:37:30 +01:00
jekkos
cfbb700f0c Add symlink for node in Dockerfile 2016-01-22 13:10:23 +01:00
FrancescoUK
95f6738d5b Fix package.json version to be 2.4 (#284) 2016-01-22 11:31:13 +00:00
FrancescoUK
0cde54e985 Merge branch 'master' into ci3 (#284) 2016-01-22 08:39:24 +00:00
jekkos
a3d3cfad61 Use Dockerfile in travis-ci setup (#284)
Set version in package.json
2016-01-22 08:18:40 +01:00
FrancescoUK
b89e9c56cc Merge branch 'master' into ci3 (#284) 2016-01-21 18:38:48 +00:00
jekkos
192955b33d Fix receipt barcode generation for docker (add php5-gd lib to Dockerfile) (#284)
Make setup scripts with CI3
2016-01-21 18:30:06 +01:00
jekkos
195c8a0601 Make docker work with 2.4 branch 2016-01-21 16:23:54 +01:00
jekkos
9048d68baf Make docker work wig 2016-01-21 16:23:17 +01:00
jekkos
2b70762b7a Use ubuntu trusty for Docker container + add startup scripts (#284)
Add Grunt concatenate for sql scripts (migrate + db init) (#289)
2016-01-20 18:47:44 +01:00
FrancescoUK
de2d034377 Update database/tables.sql script with new sessions table (#120) 2016-01-19 12:00:51 +00:00
FrancescoUK
f941a45d13 Update Copyright notice (#120) 2016-01-19 11:21:08 +00:00
FrancescoUK
4280e05679 Make the data blob NOT NULL in the SQL upgrade script (#120) 2016-01-19 08:24:58 +00:00
FrancescoUK
830f714a44 Change sessions to be stored in database and add SQL upgrade script 2.3.4 to 2.4 (#120) 2016-01-18 23:01:42 +00:00
FrancescoUK
f8c9acd88f Fix to custom fields that cannot be null on Items save (#120) 2016-01-18 20:15:24 +00:00
jekkos
d67f08d842 Add (temp) fix for cur_page conuter in CI pagination libary (#120)
Fix .htaccess file
2016-01-18 19:23:44 +00:00
jekkos
72fc36c8f5 Add (temp) fix for cur_page conuter in CI pagination libary (#284)
Fix .htaccess file
2016-01-18 20:13:07 +01:00
FrancescoUK
3c22f9069b Restore padding in pagination links, now it uses css (#120) 2016-01-17 19:02:08 +00:00
FrancescoUK
c2eaf88d74 Upgrade to CI 3.0.4 (#120) 2016-01-17 18:33:26 +00:00
FrancescoUK
b72d620b15 Added comments to some controller functions and improved spacing to improve readibility of code (#120) 2016-01-17 17:56:54 +00:00
FrancescoUK
dce413631c Make NULL to be null all over the code for consistency purposes (#120) 2016-01-16 22:08:43 +00:00
FrancescoUK
5468127e2f Further review and fixes to input->post(...) (#120) 2016-01-16 22:04:12 +00:00
FrancescoUK
d66e6ab6ef Fix to items search not working due to input->post returning null (#120) 2016-01-15 22:53:09 +00:00
FrancescoUK
a3929d2c6f Merge remote-tracking branch 'jekkos/master' into ci3
# Conflicts:
#	application/config/database.php.tmpl
2016-01-15 18:57:11 +00:00
jekkos
b5cc1542b6 Merge branch 'master' of https://github.com/jekkos/opensourcepos 2016-01-13 18:58:40 +01:00
jekkos
1a77960daa Update README.md with instructions for Docker setup 2016-01-13 18:57:44 +01:00
jekkos
301fbf4427 Merge pull request #283 from ais-one/ais-one-php7-fix
php7 fixes
2016-01-13 17:59:01 +01:00
Aaron Gong
2f0666325f PHP7 Fix
Use __construct as constructor instead of Class name
2016-01-13 15:59:37 +08:00
Aaron Gong
d0c765ce20 PHP7 fix
Change driver from mysql to mysqli
2016-01-13 15:58:20 +08:00
jekkos
a1ad99cc0d Add Dockerfile. Ospos is now containerized. Pull image from
docker build -t me/ospos git://github.com/jekkos/opensourcepos.git

then run

    docker run -it me/opensourcepos

And head to port 80 to login to a new ospos install.
2016-01-13 08:45:10 +01:00
jekkos
2d0b20499e Set correct locale for Dutch (Belgium) 2016-01-11 18:53:57 +01:00
FrancescoUK
5b01e60ed2 Merge branch 'master' #274 change into ci3 2016-01-10 13:13:55 +00:00
jekkos
26ff81ab71 Generate languages for German (Swiss) #274 2016-01-09 17:29:57 +01:00
Thorsten
da3349765a Add de_CH language (by nano-riff #274) 2016-01-09 16:46:00 +01:00
FrancescoUK
b470fbfb3d Fix merge miss in constants.php (#120) 2016-01-07 20:57:57 +00:00
FrancescoUK
1f86842757 Complete CI 3.0.3 upgrade since it wasn't done in full (#120) 2016-01-07 20:16:59 +00:00
FrancescoUK
de47b11880 Remove application/errors, upgrade Step 8 (#120) 2016-01-07 19:09:53 +00:00
FrancescoUK
6b2b3969ec EXT constant deprecated, upgrade Step 20 (#120) 2016-01-07 19:09:53 +00:00
FrancescoUK
14c5a0823b Follow upgrade guideline Step 9 (#120) 2016-01-07 19:09:53 +00:00
FrancescoUK
98b5c8da28 Fix reports error due to report.php now being Report.php (#120) 2016-01-07 19:09:53 +00:00
FrancescoUK
cd7060afed Change config language to be 'en' consistent with the OSPOS language identifiers (#120)
However language support doesn't work properly at the moment.
2016-01-07 19:09:53 +00:00
FrancescoUK
0787264f71 Improve .htaccess with some security rules (#120) 2016-01-07 19:09:53 +00:00
FrancescoUK
347609d20c Upgrade config.php and database.php.tmpl to CI 3.0.3 versions (#120)
Apparently the upgrade wasn't complete.
2016-01-07 19:09:53 +00:00
FrancescoUK
0340dc7ffc Remove application/config.php and database.php (#120) 2016-01-07 19:06:35 +00:00
Toni Haryanto
d8057830c6 Upgrade to Code Igniter 3.0.3 (#120) 2016-01-07 19:06:35 +00:00
jekkos
4ddde65b41 Bump version to 2.3.4
Update dutch translation for company_name
Fix timeformat for date filtering in items module
2016-01-07 19:53:52 +01:00
jekkos
c408afad67 Cleanup migration script, mostly working now (#262) 2016-01-05 17:41:43 +01:00
jekkos
b1ba7d0afe Split up database.sql in scripts for tables and contraints.
Use both new scripts in phppos_migrate.sql
2016-01-05 07:41:49 +01:00
FrancescoUK
31a223cece Merge pull request #272 from daN4cat/master
#271 fix to error witn xampp due to a spurious "git"
2016-01-04 22:13:29 +00:00
FrancescoUK
3bd778737f #271 fix to error witn xampp due to a spurious "git" 2016-01-04 22:09:38 +00:00
jekkos
648251978e Merge pull request #270 from daN4cat/master
Fixes to #265, #267 and #269
2016-01-04 22:13:25 +01:00
FrancescoUK
240c96a185 #267 remove warning due to junk dirs being inside a language/xx one 2016-01-04 19:44:15 +00:00
FrancescoUK
810000e37c #265 do not override 404 handler in routes.php 2016-01-04 18:23:16 +00:00
FrancescoUK
6aa5126bf0 #269 fix https error issue 2016-01-04 17:55:00 +00:00
jekkos
90591382ce Further cleanup phppos migration script (#262) 2016-01-04 17:26:42 +01:00
jekkos
9e2d89c528 Show discounted price instead of discount itself on receipt 2016-01-04 17:19:50 +01:00
jekkos
1601d679fd Takings not using proper permission (#262) 2016-01-04 17:04:33 +01:00
jekkos
aa0409af48 Add phppos migration script (untested) #258 2016-01-01 17:10:43 +01:00
jekkos
ad194bd4c0 Fix typo in english translation (#260) 2016-01-01 16:46:58 +01:00
jekkos
1779685d31 Encode text before storing it as session variable (#254) 2015-12-30 08:10:14 +01:00
jekkos
51df82f35e Update WHATS_NEW 2015-12-30 07:42:27 +01:00
jekkos
bf0622d7fa Show discounted price per item line 2015-12-26 15:26:32 +01:00
jekkos
da25281df9 Show discounted price per item line 2015-12-26 15:21:54 +01:00
jekkos
2f52d083e8 Update changelog for 2.3.3 2015-12-26 13:13:47 +01:00
jekkos
eb63f49db7 Add barcode generation for EAN8 and EAN13 (#111, #246)
Add config option to use generation when generating barcodes for an
Generate propery barcode with configured format if item_number empty
2015-12-26 11:42:58 +01:00
jekkos
c007513fc8 Fix barcode generation error for empty item numbers 2015-12-13 15:57:03 +01:00
jekkos
9f5782e0ba Add validation for barcode types (#111, #246)
Fallback to code128 if invalid EAN8 or EAN13 is detected
2015-12-13 15:51:25 +01:00
jekkos
2f14a0f868 Fix typo in store language line (#247) 2015-12-13 14:15:43 +01:00
jekkos
fb924daa81 Show reoder level in items form when editing (#241) 2015-12-04 18:13:05 +01:00
jekkos
1bb9acdc37 Fix for adding/deleting stock locations (#242) 2015-12-04 18:09:51 +01:00
jekkos
16d5dfce77 Merge pull request #244 from kamaroly/master
Fixed Severity: Notice Message: Only variable references should be returned by reference Filename: core/Common.php Line Number: 257
2015-12-03 23:06:22 +01:00
Kamaro Lambert
afa203e73c Fixed error Only variable references should be returned by reference
Severity: Notice Message: Only variable references should be returned by reference Filename: core/Common.php Line Number: 257
2015-11-30 23:30:56 +02:00
jekkos
bc85391734 Submit form when hitting enter in search field for items module (#187) 2015-11-09 11:15:42 +01:00
jekkos
7c2cc9646d Don't override custom invoice numbers (#223) 2015-11-09 10:46:35 +01:00
jekkos
c0303cc873 Merge branch 'master' of https://github.com/asakpke/RoshanTech.com-POS 2015-10-20 22:01:37 +02:00
jekkos
0ddd80dc2c Regenerate thai language (#205) 2015-10-20 22:00:18 +02:00
songwutk
af582056d7 update sales_lang.csv : Thai
"sales_suspend_sale" too long in button, so change word fit.
2015-10-20 22:00:18 +02:00
jekkos
56859a77ab Fix path for jquery-ui icons (#207) 2015-10-20 13:23:20 +02:00
jekkos
ec5846e1eb Make receivings datetimepicker consistent with sales form (#169) 2015-10-18 21:37:49 +02:00
jekkos
6d5c192447 Add jquery-ui slider for datepicker #169 2015-10-17 00:22:57 +02:00
FrancescoUK
1b90ac14d2 #169 regenerate dist/ js scripts adding datetimepicker jQuery plugin 2015-10-15 19:12:50 +01:00
FrancescoUK
9c775ca44b #169 Add date and time picker to receivings/form
Some code indentation for better readibility.
2015-10-15 18:03:46 +01:00
FrancescoUK
0d87fc51d9 #169 Add a date time picker to sales/form such user can edit transaction date not in a manual manner
Needs more testing with different date & time formats and sorting out of min.js
2015-10-14 11:40:59 +01:00
jekkos
c4c7d9d792 Merge pull request #169 from marteserede/master
Fix dateformat value in SQL-Query app_config
2015-10-12 09:36:37 +02:00
jekkos
455a5f59d1 Merge pull request #199 from souparno/bug-condition-statement
if condition statement missing, may result in server error
2015-10-10 15:09:10 +02:00
souparno
9cf5d5734f if condition statement missing, may result in server error 2015-10-10 13:11:32 +05:30
jekkos
43b8d32efc Don't use empty on php expression (#196)
Signed-off-by: jekkos <jeroen.peelaerts@gmail.com>
2015-10-09 17:11:24 +02:00
jekkos
0c25b01666 Add default line endings 2015-10-09 11:43:19 +02:00
jekkos
1fd7fda645 Fix totals calculation when adding item multiple times (issue #165)
Fix typo in script comments
2015-10-09 10:24:38 +02:00
jekkos
3eb798917d Clear out empty sales and receivings comments on database upgrade
(#192)
2015-10-08 18:43:52 +02:00
jekkos
64023f6eec Avoid comments coercion to 0 when suspending sales (#192)
Invoice number in suspended sales should not be unique
2015-10-08 18:29:55 +02:00
jekkos
d8a369f39d Remove unique constriant from invoice_number column in suspended_sales 2015-10-08 18:29:55 +02:00
jekkos
98b0df9e80 Merge pull request #194 from daN4cat/master
#192 Remove 0 from comments
2015-10-07 13:11:18 +02:00
FrancescoUK
763a9e73fa #192 Remove 0 from comments
Checked in library get_comment if the string is null and return a '' if that's the case. That removes the 0 from sale comments
2015-10-07 09:37:27 +01:00
jekkos
df5a0448aa Merge pull request #193 from daN4cat/master
#192 Fix to comment not saved when suspending a Sale
2015-10-07 10:07:31 +02:00
Aamir Shahzad
76365afbd2 Added my name, ID and website in COPING 2015-10-06 22:58:37 +05:00
Aamir Shahzad
6e27b4c9ec Extra comments removed 2015-10-06 22:52:00 +05:00
FrancescoUK
a7e2ea5fa7 #192 Fix to comment not saved when suspending a Sale
The issue was that for a strange reason the comment was fetched directly from the form instead of using the sale_lib->get_comment like in the rest of the code.
2015-10-06 17:55:58 +01:00
Aamir Shahzad
986bbc9b36 Better Navigation with TAB key on Sales page 2015-10-06 03:31:57 +05:00
FrancescoUK
03f6d234bc #175 Fix inverted sorting issue 2015-09-28 22:47:16 +01:00
FrancescoUK
79361c976c Refactoring around items and sales search 2015-09-28 19:21:35 +01:00
FrancescoUK
1296f393d7 #175 Add pagination links 2015-09-28 18:24:02 +01:00
FrancescoUK
e49dd4e0b9 #179 Make Items Kit icon visible on home page
Remove the if statement that was in the end preventing any module having a _ to be displayed in the home page.
This solution is consistent with navigation bar one.

Also done some indentation in header and footer to improve readibility.
2015-09-28 13:12:22 +01:00
FrancescoUK
319545b989 #180 Fix to allowing a sale suspend before any payment
Reformatted the register code to increase readibility.
Please note that the real change is just moving the Suspend Sale button from finish_sale_form to cancel_sale_form, adding float:left style to Cancel button and changing the javascript for Suspend sale to address the correct form.
2015-09-28 08:59:22 +01:00
FrancescoUK
4a3e9ef79d #167 Limit description and serialnumber to 30 char before saving item to sale or suspended sale tables 2015-09-27 18:08:10 +01:00
FrancescoUK
e566223db9 #168 Items Kit: search by KIT id 2015-09-27 16:49:50 +01:00
FrancescoUK
22406d0c3a Merge pull request #176 from daN4cat/master
#165 Fix invoice wrong date on sale completion
2015-09-27 16:39:52 +01:00
FrancescoUK
8bb108503c #165 Fix invoice wrong date on sale completion 2015-09-25 19:02:03 +01:00
jekkos
1eb43d2a74 Merge branch 'master' of https://github.com/jekkos/opensourcepos 2015-09-24 22:02:23 +02:00
jekkos
56f7be21ae Merge pull request #171 from souparno/bug-fix-reset-database
reset database syntax contains check, so as it can be used to create…
2015-09-23 22:57:46 +02:00
FrancescoUK
ba6ecf0319 #132 Fix date range missing end date items 2015-09-23 18:04:04 +01:00
souparno
47c8d6c0bd reset database syntax contains check, so as it ccan be used to create the database for the first time 2015-09-23 21:29:17 +05:30
FrancescoUK
d9c49c1a72 #132 revert a modification in item model 2015-09-23 13:00:34 +01:00
FrancescoUK
5af7473769 #132 Set 1-1-2010 as starting date for the date range 2015-09-22 19:01:40 +01:00
FrancescoUK
aa9cd1fe6a #132 Make sure we don't show multiple times the same item given a range 2015-09-22 18:38:21 +01:00
FrancescoUK
58899ca4cf #132 Added date range in item model. Smoke test fine, more to test 2015-09-22 18:38:20 +01:00
FrancescoUK
404bc47e96 #132 Added date picker to Items manage, added date range to controller + model 2015-09-22 18:38:19 +01:00
jekkos
7dfcec394f Substitute invoice numbers on complete once again (#163, #150) 2015-09-22 18:21:19 +02:00
jekkos
0646200f6c Increase colspan for suppliers list
Multiple item kit price with quantity at hand
2015-09-22 07:40:48 +02:00
Martes Erede
2bc7b688ff Fix dateformat value in SQL-Query app_config 2015-09-22 07:17:57 +02:00
FrancescoUK
9309d7f15b Fix to COPYING file to follow correct license & copyright format (see http://mit-license.org/) 2015-09-20 21:48:23 +01:00
FrancescoUK
87153fc70e Add time ranges for quick report searches 2015-09-20 15:03:16 +01:00
FrancescoUK
ff12531b42 Revert "#159 Fixed typo in comment"
This reverts commit f18b54a758.
2015-09-20 14:54:54 +01:00
FrancescoUK
f18b54a758 #159 Fixed typo in comment 2015-09-20 09:18:57 +01:00
FrancescoUK
9a47b448ac #153 Set private internal functions 2015-09-20 09:18:03 +01:00
FrancescoUK
569bf4db69 #111 Change databse.sql barcode defaults 2015-09-19 17:52:14 +01:00
FrancescoUK
f97e02c3d4 Items: allow custom field suggestion and search if checkbox is ticked 2015-09-19 17:46:28 +01:00
FrancescoUK
893345d334 #111 Change barcode types from numbers to strings for better code readability 2015-09-19 17:44:43 +01:00
FrancescoUK
80fcede1ca #152 Remove Alain name and add it to the COPYING under the Copyright section 2015-09-19 17:40:00 +01:00
FrancescoUK
461de2f048 #162 fix further issues due to cost & retail prices added to the table 2015-09-19 15:23:16 +01:00
FrancescoUK
4106378a6c Changed CONCAT and use of " & ' 2015-09-19 13:19:49 +01:00
FrancescoUK
dfb556b27b Fixed typo in comment 2015-09-19 12:43:08 +01:00
FrancescoUK
41607c23bd Fix to #161 when searching a Kit by name in Sales 2015-09-19 12:38:21 +01:00
FrancescoUK
a29b8c2a62 Added Kit prices to table view and barcode #162
Some spacing and retouching here and there.
2015-09-19 12:05:44 +01:00
FrancescoUK
dc45b77573 Fix to Gift card totals issue #159
Various indentation adjustments
2015-09-19 09:22:24 +01:00
FrancescoUK
ad948ac81e Fix to rebase/merge mistake with Barcode_lib 2015-09-18 18:05:43 +01:00
FrancescoUK
eee7f137ef Fix to rebase/merge mistake with Barcode_lib 2015-09-18 18:33:32 +02:00
jekkos
2237c332b9 Derive barcode id from item array 2015-09-18 18:33:22 +02:00
FrancescoUK
d5cf608bac Fix to #152 remove name of people from code comment and add them to COPYING license file 2015-09-18 18:33:22 +02:00
FrancescoUK
96973da280 Item kits issue #155 fix
Added Kit Id column to table view
2015-09-18 18:33:22 +02:00
FrancescoUK
bca4386aed Fix to sale and receiving receipt barcode 2015-09-18 18:33:22 +02:00
FrancescoUK
27f89a63be remove the - from language/id/common_lang.php 2015-09-17 22:00:06 +02:00
FrancescoUK
3f5d0b781b Fix to $lang["common_last_page"] issue #156 2015-09-17 22:00:06 +02:00
FrancescoUK
0984cdbfc4 Fix to sale and receiving receipt barcode 2015-09-17 22:00:06 +02:00
FrancescoUK
c48106f2f5 Fix items view search issue having a ' in the string. Bug #149 2015-09-17 21:59:34 +02:00
jekkos
e1ddcbbb5b Rename Item_quantities model to Item_quantity
Rename Stock_locations model to Stock_location
Fix barcode generation in receipts (sales + receivings)
2015-09-17 21:59:33 +02:00
FrancescoUK
2b7ceb4228 Move Print button in sale/manage (Takings) to top right 2015-09-17 21:59:33 +02:00
jekkos
3c89416b83 Update languages 2015-09-15 23:21:59 +02:00
jekkos
290aee4671 Merge branch 'patch-13' of https://github.com/songwutk/opensourcepos 2015-09-15 23:01:10 +02:00
FrancescoUK
10ea821b2a Leave item_number empty if Code39/128 is selected
Added Empty UPC checkbox in Items view to easily select empty items that need barcode code generation and in case of EAN8/13 fill in item_number
Added language for Empty UPC string
Indentation/spacing adjustment
2015-09-15 17:49:51 +01:00
FrancescoUK
cf0276c772 If item_number (UPC/EAN/ISBN) is empty and a new barcode is generated save it in the item_number field and update the item entry
Refactoring of barcode_lib
Some code alignment adjustment here and there
2015-09-15 11:13:58 +01:00
FrancescoUK
8f2740d349 Centred barcode image
Moved $data to base class and added getData
Fixed indentation
2015-09-14 23:20:20 +01:00
FrancescoUK
eb71dbc300 Fix EAN 13 typos + set pix per bar
Add EAN 8 support
2015-09-14 23:20:19 +01:00
FrancescoUK
68915dd28c Ean13 code generation implementation 2015-09-14 23:20:19 +01:00
FrancescoUK
93d8775ac5 Remove extra comma from end of array 2015-09-14 23:20:18 +01:00
FrancescoUK
807d48ac7b Add EAN13 in Barcode lib
Sketched EAN13 class code
Fix typo
2015-09-14 23:20:17 +01:00
FrancescoUK
79fd666bff Add Ean13 file and class
Fixed typos
2015-09-14 23:20:16 +01:00
FrancescoUK
393ac9bb97 Fixed some minor issues 2015-09-14 23:20:15 +01:00
songwutk
ac95e1f7d8 Update common_lang.csv
Fill thai blank topic.
2015-09-14 16:30:40 +07:00
jekkos
c843c174ed Initialize table sorting if this didn't happen when the page was loaded
initially (#139)
2015-09-14 08:26:41 +02:00
jekkos
d6f052f082 Add default translation in other languages as well (#142) 2015-09-12 20:55:54 +02:00
jekkos
a78ef2fb08 Merge branch 'master' of https://github.com/marteserede/opensourcepos 2015-09-12 20:16:46 +02:00
Martes Erede
4cbbd3a6f7 [#32] using to_currency for currency values in table-data 2015-09-11 16:16:34 +08:00
Martes Erede
4a8c3a83b3 [#32] adding new language-strings and running generate_languages.php 2015-09-11 15:59:27 +08:00
Martes Erede
308be8079f [#32] improving the inventory summary report, including total inventory value 2015-09-11 15:58:57 +08:00
jekkos
335ced948e Remove unneeded fields from sales_temp table 2015-09-09 23:20:31 +02:00
jekkos
b41a006a79 Fix duplicated payment amounts (#136) 2015-09-09 23:09:28 +02:00
jekkos
506aced23e amount_due and change_due were incorrect if multiple items in sale
(#136)
2015-09-09 19:25:44 +02:00
jekkos
e9ea2ba792 Create temp table when necessary 2015-09-07 22:53:19 +02:00
jekkos
2c5f3ee759 Add grunt-cache-breaker plugin (#137)
Fix tax inclusive pricing for daily sales overview (#136)
Fix table sorting when using search in daily sales overview (#131)
2015-09-07 22:15:41 +02:00
jekkos
b680cdff36 Fix table sorting after loading search results 2015-09-07 08:35:59 +02:00
jekkos
80f7966d1c Add default dateformat and timeformat in data scripts 2015-09-06 15:53:11 +02:00
jekkos
e62df579f1 Keep last row in position even after filtering 2015-09-06 13:33:12 +02:00
jekkos
2b6b3914a7 Mark totals row as static (keep it at bottom of the table issue #131) 2015-09-06 13:19:32 +02:00
jekkos
756b675e2b use configured dateformat for jquery ui date parser
disable sorting on columns 0 and 9 (no data there)
default sorting enabled on column 1
2015-09-05 15:05:20 +02:00
jekkos
1d65ceaccf Merge branch 'master' of https://github.com/daN4cat/opensourcepos 2015-09-05 09:52:48 +02:00
jekkos
0c92cc661f Remove date field type for filters 2015-09-05 09:52:30 +02:00
FrancescoUK
c87a936e22 Fix language issue 2015-09-04 23:24:53 +01:00
FrancescoUK
c041a18624 Added Print button to print the current view 2015-09-04 22:51:12 +01:00
jekkos
ac3aab51fa Fix payment summary update on search 2015-09-04 23:35:23 +02:00
jekkos
9281995029 Fix payment summary update on search
Merge branch 'master' of https://github.com/daN4cat/opensourcepos into daN4cat-master
2015-09-04 23:14:06 +02:00
jekkos
f36c700129 Add date filtering in sales overview
Add jquery-ui datepicker
Format dates in sale and receiving forms according to configured
dateformat and timeformat settings
Add missing translations in sale (click delete when not selecting
anything)
Add search box back to search for customer sales and ticket numbers
2015-09-04 22:09:24 +02:00
FrancescoUK
d35ef071bb - Added cash only filter
- Set to 'all' the sale_type so even returns are shown (we might want to add a drop down filter for this too)
2015-09-04 19:30:43 +01:00
FrancescoUK
852f83a8f6 Rename Overview to be Takings (normally used word in retail world) 2015-09-04 10:31:02 +01:00
FrancescoUK
7bbd651779 Remove commented out lines 2015-09-04 10:11:02 +01:00
jekkos
a624d21fcd Fix sale detail edit
Make last row in sale table bold (css)
Add default date range between yesterday and today
Temporarily comment out payment type listing in table itself (tbd)
2015-09-04 08:44:20 +02:00
jekkos
9a2cafc1b1 Review payment type overview
Add some padding to bottom of content area
Fix invoice only searching
Make start and end dates optional
2015-09-03 23:35:57 +02:00
FrancescoUK
b226a40efc Make Gift Card one payment item and not multiple ones in Sale/Overview and in Summary Payment report.
Fixed also the colspan and set to 10.
2015-09-03 19:18:15 +01:00
FrancescoUK
0b8a263443 Sale Overview change:
- set today as view date and comment out the debug dates
2015-09-02 23:22:44 +01:00
FrancescoUK
75fa706299 Sale Overview change
- Refactor and optimise payments part (added now amount to detail sale report too)
- fix some </tr> that should have been </td>
- minor indentation fixes

NOTE: The only_invoices checkbox is not responsive
2015-09-02 23:16:26 +01:00
FrancescoUK
c88f37f8e7 Change Sale Overview to become a daily takings view with totals. 2015-09-01 23:37:26 +01:00
jekkos
3e24b9caff Remove non working datepicker icon
Remove in_array check for duplicated company_name
2015-08-24 21:52:41 +02:00
jekkos
517363b485 Fix deleted checkbox in items form
Clear suggestion cache when toggling deleted filter
2015-08-18 18:10:00 +02:00
jekkos
011caf375f Apply is_deleted in item suggestion search 2015-08-18 08:35:21 +02:00
FrancescoUK
9f1485cfe5 Added Cost & Count to reports, improved Items suggest, added copyrights and some fixes
Added cost and count to reports

- Added cost in temp table, reports at bottom of page and in each
summary and detailed report
- Added count in all the reports
- Renamed some report fields name to make it generic and consistent
across views
- Fixed few issues here and there
- Adjusted some code indentation

NOTE: Proper translation of word Cost is required in all the languages
but English

Improved Items suggestion

- Added select to restrict results fields
- Added distinct in suppliers instead of the array match
- Added $is_deleted in model part but is not still effective

Add copyright statements in COPYING license file

- Added jekkos and FrancescoUK / daN4cat

Other people that contributed should add their statements.
2015-08-17 11:13:04 +01:00
jekkos
0336fe6298 Stop showing duplicate suggestions for company_name 2015-08-16 15:52:25 +02:00
jekkos
4ab2ec1b5b Add supplier attribute search for items (pull request #106)
Add company name to supplier column in summary report
2015-08-16 15:41:39 +02:00
jekkos
eb3657e76f Fix upgrade script (2) 2015-08-13 22:53:52 +02:00
jekkos
61206547cb FIll in empty location_ids for existing data 2015-08-12 15:10:08 +02:00
songwutk
9ae6e3242b Update Thai translate (Pull requests #98, #99, #100, #101, #102) 2015-08-11 18:49:28 +02:00
jekkos
7f5fe9efde Upgrade grunt-mocha-webdriver 2015-08-10 15:32:00 +02:00
jekkos
9e189a503e Merge pull request #105 from gabrielperezs/master
Solved to issues in this request. Spanish support, and temp table creation.
2015-08-10 11:03:58 +02:00
Gabriel Pérez S
da41aec0d1 Spanish traslation 2015-08-10 10:26:11 +02:00
Gabriel Pérez S
03b9924d57 Create table if not exists 2015-08-10 10:14:03 +02:00
jekkos
87222611e6 Remove duplicated last name for suppliers 2015-08-10 09:30:01 +02:00
FrancescoUK
cc06d93d30 Remove LICENSE file (use COPYING now instead)
Update agency_name constraint in update script
Show supplier_id in suppliers for use with item excel import
2015-08-09 12:01:09 +02:00
jekkos
7926a05948 Regenerate language files
Remove committed merge conflict in giftcard_numbering.js
Add sql upgrade script for 2.3.3
2015-08-07 17:17:53 +02:00
FrancescoUK
d7ff4a371c Fix to the License naming clash issue
- Instead of LICENSE used COPYING (it's accepted see:
http://www.theregister.co.uk/2013/04/18/github_licensing_study)
- removed license.txt inside license dir to avoid unnecessary
duplications or clash of license of the ospos code. COPYING is anyway a
rename of LICENSE that was a newer version than the one in
license/license.txt
2015-08-07 15:02:15 +02:00
FrancescoUK
aa144b43eb Fixed invoice date bug
The invoice date was not showing the correct date and neither following
the selected format
2015-08-07 15:02:15 +02:00
FrancescoUK
f806ea84b3 Update to Suppliers feature
- added Agency name to identify agent or distributor behind a brand
- updated view to show supplier ID useful for item imports via excel
sheet
- update database table
- updated language although missing translations
- various bit an pieces missing due to failed rebase
2015-08-07 15:02:15 +02:00
jekkos
17ac5b1837 Bump version number to 2.3.3 2015-07-25 12:03:04 +02:00
FrancescoUK
8bcd2a69dc Add better locale config support (pull request #93)
I've added in this commit the following:

- the means to configure date and time format
- date and time is shown with the selected formats
- a locale tab in the configuration page
- changed location tab to be stock tab and the same for the file names
- translation strings (only English) for the case

NOTE 1: Not related to Locale support but I removed an extra <div
class="field_row clearfix"> from general_config.php

NOTE 2: There is no rules in the submit checks of Locale. Probably this
is something to iron out a bit

NOTE 3: It would be probably nice to add a button in the Locale tab to
regenerate all the translation files in case somebody updates the
translations .csv

Fixed Local config

- Removed text in Red as really not mandatory configuration
- Added time format in 24h and 12h

Updated General Config tab

- reorganised the layout grouping things like company info, taxes and
invoice formating
- increased the limit of max lines per page to 1000 because 50 is too
little. People can do what they want with up to 1000
2015-07-25 12:02:10 +02:00
jekkos
e0f80528f4 Fix report generation regression 2015-07-15 18:14:58 +02:00
jekkos
491d8d0b72 Increase testcase delays
Keep keys order when reversing array for stock location options
2015-07-14 18:17:54 +02:00
jekkos
80dcc2f63f Increase waiting timeout for phantomjs test
Revert old database script
Add styling rules for number typed input fields
2015-07-14 12:36:43 +02:00
jekkos
92da2749ad Adapt report summary to chosen stock location
Update release notes
2015-07-13 18:39:50 +02:00
jekkos
83a297cf15 Add stock location as filter to detailed sales + receivings reports 2015-07-10 19:04:52 +02:00
jekkos
70b7a03c63 Show item location next to quantity purchased in detailed receiving and
sale report
Remove duplicate suggestions in sale and receiving module
Show item location on receipt if multiple are configured
Check if item location is allowed when adding items in sale or receiving
2015-07-08 21:41:41 +02:00
jekkos
b1f1feda48 Show alternative description on invoice only if not empty 2015-07-01 08:29:53 +02:00
jekkos
48fe4aa4cf Show item alternative description if available
Show sale instead of customer comments on invoice
2015-06-30 23:08:56 +02:00
jekkos
0788f4eef9 Set precision to 5 decimals (constants.php)
Show item description if serialized (invoice.php)
2015-06-28 14:20:39 +02:00
jekkos
fc5f097820 Add proper validation for module grants (issue #87) 2015-06-23 23:32:56 +02:00
jekkos
df7907fb8f Don't lose date precision when editing sale or receiving
Extend date validator to allow timestamps as well (sale + receiving)
Fix receiving edit
2015-06-20 20:58:41 +02:00
jekkos
46b5c18aca Add receiving_quantity field to ospos_receivings (issue #78)
Detailed receiving now correctly shows purchased quantity
2015-06-20 14:59:37 +02:00
jekkos
5c1ecf4618 Update .gitattributes with merge strategy for specific files 2015-05-25 13:28:03 +02:00
jekkos
ccc2af2a26 Show tax exclusive subtotal in case of tax inclusive pricing 2015-05-23 16:51:42 +02:00
jekkos
872d6857cd Add decent delay for waitForElement in wd test 2015-05-22 23:29:25 +02:00
jekkos
658792c1fc Fix invoice return 2015-05-22 19:26:56 +02:00
jekkos
4a324fbf4c Fix inventory update on deleting sale (issue #80) 2015-05-22 19:26:56 +02:00
jekkos
97d268e323 Tax summary report calculation fixes 2015-05-22 07:54:07 +02:00
jekkos
ca05583ec3 Merge branch 'master' of https://github.com/jekkos/opensourcepos 2015-05-21 22:12:50 +02:00
jekkos
7d0a6dc7ae Merge branch 'feature/travis-ci' 2015-05-21 22:12:30 +02:00
jekkos
3ba3a3dea0 Merge branch 'travis-ci-setup'
Conflicts:
	test/login.js
2015-05-21 22:10:28 +02:00
jekkos
1ad08c014f Fix giftcard numbering issue #65 2015-05-21 22:00:33 +02:00
jekkos
3936f2dcf3 Add giftcard numbering test 2015-05-20 18:35:10 +02:00
jekkos
fcc71a5f74 Sale reporting now takes tax inclusive pricing into account 2015-05-19 22:44:03 +02:00
jekkos
46f6824199 Fix rounding issues with tax inclusive pricing
Add autocomplete in return mode
2015-05-19 20:46:42 +02:00
jekkos
7c0f884872 Remove trailing space (issue #76) 2015-05-19 09:40:30 +02:00
jekkos
15493ae58e Set correct url 2015-05-17 22:36:00 +02:00
jekkos
ac7c0241e5 Merge pull request #79 from nalamapu/patch-1
Update general_config.php
2015-05-17 21:44:32 +02:00
A.K.M. Nurul Alam
fd933e3ed6 Update general_config.php
'Comma' is the original spelling in English.
2015-05-16 12:14:21 +06:00
jekkos
6a2a5278b8 Fix general test setup (finally)
Add basic selenium test for application login
Adapt default database template file
Add correct db settings for travis-ci
2015-05-15 00:47:25 +02:00
jekkos
687d4c06c6 Fix general test setup (finally)
Add basic selenium test for application login
Adapt default database template file
Add correct db settings for travis-ci
2015-05-15 00:45:55 +02:00
jekkos
19f7c6aa57 Initialize mysql on travis 2015-05-13 00:14:11 +02:00
jekkos
ccf1a6209f Set travis.yml language to php (node should be default) 2015-05-12 23:44:43 +02:00
jekkos
4db6f28c76 Try to fix php-fpm absence (3) 2015-05-12 22:26:37 +02:00
jekkos
3e08ec99a5 Try to fix php-fpm absence (2) 2015-05-12 13:59:08 +02:00
jekkos
4142f0fc71 Try to fix php-fpm absence 2015-05-12 13:53:16 +02:00
jekkos
1d9a0bc8b6 Add travis-ci-apache 2015-05-12 13:52:32 +02:00
jekkos
a58510c541 Add apache2 + mysql setup for travis 2015-05-12 13:46:34 +02:00
jekkos
b2a37b7ded Add category column again 2015-05-01 12:11:49 +02:00
jekkos
69528baa21 Merge pull request #60
Update common_lang.csv

Spanish translation to CSV files. Spanish translation updated - xepaez 20150424

Added translation labels missing in CSV import forms.
2015-04-27 19:07:28 +02:00
jekkos
c5defaab35 Empty person_id field in giftcard edit if id is null 2015-04-27 19:07:28 +02:00
jekkos
af7910a6a8 Show giftcard number instead of id in edit form 2015-04-27 19:07:28 +02:00
jekkos
86eb397866 Enable sorting on giftcard number
Add default stock permissions for sales and receivings in upgrade script
2015-04-27 19:07:28 +02:00
jekkos
9b1785df01 Correct obvious punctuation error in item form.php 2015-04-27 19:07:28 +02:00
jekkos
8f09a32ef5 Correct latest alter table statements for 2015-04-27 19:07:28 +02:00
jekkos
c6d9a8a994 Show correct quantity in item form
invert ternary condittions to show correct quantity at any time
2015-04-27 19:07:28 +02:00
jekkos
54855849b3 Show item number in order report
Conflicts:

	application/controllers/reports.php
	database/database.sql
2015-04-27 19:07:28 +02:00
jekkos
7a9590359e Fix currency symbol when no dollars are chosen 2015-04-27 19:07:28 +02:00
jekkos
11eed40718 Update receivings.php
clear invoice number when changing receive mode
2015-04-27 19:07:28 +02:00
jekkos
bc7eeb5d30 Do not clear session state when switching sale or receiving modes
Set receiving quantity and discount validation to numeric instead of
integer
2015-04-27 19:07:27 +02:00
jekkos
4916f98bcb Fix currency symbol when no dollars are chosen 2015-04-14 13:04:46 +02:00
jekkos
c85dd1660e Add .idea folder to .gitignore 2015-04-13 18:21:15 +02:00
jekkos
08c0c2c63f Giftcard remaining balance is always shown in cash register
Giftcard person_id is optional now
Add some extra translations for giftcard warnings

Conflicts:

	database/2.3.1_to_2.3.2.sql
2015-04-09 18:28:57 +02:00
jekkos
81c24d7151 Do not clear sale and receiving libs when switching modes 2015-04-09 18:28:57 +02:00
jekkos
3d62a73c8c Report subpermission labels are shown properly in employee form now
Fix logic to show correct report subpermissions
2015-04-09 18:28:53 +02:00
jekkos
b8fba91244 Show tax names and amounts in invoice and invoice_email views
Fix invoice numbering for $YCO after update
2015-04-09 18:28:23 +02:00
jekkos
80dbe6efa7 Company name can be DEFAULT NULL in database 2015-04-09 07:18:50 +02:00
jekkos
3f46f1aca9 Merge branch 'master' of https://github.com/jekkos/opensourcepos 2015-04-08 21:56:18 +02:00
jekkos
26fddcf8d4 Add custom repository for grunt-script-link-tags 2015-04-08 13:47:16 +02:00
jekkos
7a4df2a050 Fixup db script 2015-04-07 19:13:45 +02:00
jekkos
de3d7ce726 Add new language line for check_remainder 2015-04-07 19:10:46 +02:00
jekkos
4ffc5351af Add customer as a required field for giftcard
Show giftcard remainder if at least one payment is done using giftcard
2015-04-07 17:28:44 +02:00
jekkos
3f14c51c97 Update travis.yml 2015-04-06 16:55:46 +02:00
jekkos
e9b4e57c06 Add company_name column to ospos_customers 2015-04-06 15:47:38 +02:00
jekkos
70bb02c753 Change button order (default is submit now) 2015-04-06 15:45:49 +02:00
jekkos
6f7090d7ba Upgrade jquery.color
Conflicts:

	dist/opensourcepos.js
	dist/opensourcepos.min.js
2015-04-06 15:42:57 +02:00
jekkos
43ecda44e0 Receipt totals layout improvement (show top border when no discount or
tax section present)
Remove unexistant script include from invoice_email template
2015-04-06 15:38:52 +02:00
jekkos
04abb716fa Merge branch 'master' of https://github.com/jekkos/opensourcepos
Conflicts:
	dist/opensourcepos.min.js
2015-04-06 15:16:43 +02:00
jekkos
95ae2766cd Giftcard get_all parameters were in opposite order
Invoice layout improvements
2015-04-06 15:15:40 +02:00
jekkos
f45f75e458 Add functionality to keep item edit form open for easier working (in
sales, receivings and items module)
Refactor duplicated custom field code in item form
2015-04-06 15:15:40 +02:00
jekkos
b85b9d37a1 Add company_name field to customers for invoicing purposes
Total discount is now shown as one amount in receipt

Conflicts:

	application/controllers/sales.php

Conflicts:

	database/migrate_phppos.sql
2015-04-06 15:15:34 +02:00
jekkos
ebd473ea11 Update README.md
Add build status to Readme.md
2015-04-06 15:15:20 +02:00
jekkos
7d7ee83ef4 Add test folder + basic unit test setup
Add grunt mochaWebdriver plugin for phantomJs test

Conflicts:

	.travis.yml
	dist/opensourcepos.min.js
2015-04-06 15:15:14 +02:00
jekkos
27c6f275d1 Set focus to scan field when refreshing receivings module 2015-04-06 15:13:42 +02:00
jekkos
39edf5ccdb Add basic unit tests 2015-04-06 15:13:36 +02:00
jekkos
90c4df7f2a Update README.md
Add build status to Readme.md
2015-04-01 00:01:19 +02:00
jekkos
ae99555fec Add test folder + basic unit test setup
Add grunt mochaWebdriver plugin for phantomJs test
2015-03-31 23:58:05 +02:00
jekkos
8ff742085a Multiple total disconuts are shown correctly now 2015-03-31 21:56:44 +02:00
jekkos
86bf8866f0 Do duplicated cookie check before echo'ing output in items (fix for #49) 2015-03-31 16:33:29 +02:00
jekkos
a8f353de9c Add constraint for trans_location foreign key
Use Code128 for receipt barcodes in sales and receivings
2015-03-31 15:05:30 +02:00
jekkos
9b98f15f69 Receiving cancel delete should work now (confirm result was ignored)
receivings_items will now store the correct quantity_purchased when
using a value > 1 for receiving_quantity
Receiving delete message corrected
2015-03-30 16:37:26 +02:00
jekkos
138d1cbeef Remove duplicated click handlers on add/remove location 2015-03-26 13:00:08 +01:00
jekkos
9ead6fcca9 Add mochaSelenium config in Gruntfile (tests not running yet) 2015-03-20 13:50:14 +01:00
jekkos
ced5ca4e2e Make invoice numbers more consistent 2015-03-17 18:05:43 +01:00
jekkos
ca2c9a6ae4 Item refresh fix for issue #42
Invoice number in email message are denoted with $INV
Sale id's in email message are denoted with POS $sale_id
2015-03-17 17:41:12 +01:00
jekkos
cae9d3b67e Remove tags:css task in Grunfile (css files are imported the old way
again)
Fix sale delete confirmation in edit dialog
Add bower.json (still unused)
Fix autogenerated javascript load order
2015-03-17 16:47:49 +01:00
jekkos-t520
aa65593ce3 Add Gruntfile.js for concat+uglification of javascript
Enable debug=true cookie for full source inclusion in header.php
Add template in header.php to inject generated javascript files
2015-03-15 16:56:37 +01:00
jekkos-t520
c10860a005 Intercept pagination link click when search field is empty and filtering
checkboxes are set
2015-03-11 08:36:20 +01:00
jekkos-t520
a39e12e76d Externalize nominatim autcomplete functionality
Add chosen language to nominatim request
2015-03-10 14:37:36 +01:00
jekkos-t520
b29cb85b9b Add ticket suggestions for #40 2015-03-07 22:41:03 +01:00
jekkos-t520
6faaec53d6 Fix duplicate item suggestion issue #38
Fix return ticket suggestion issue #38
Fix discounted_total after hitting edit in sales register
2015-03-07 21:55:15 +01:00
jekkos-t520
b29ec8ef31 Fix show discounts functionality
Review tax price calculations
Remove tax exclusive price in invoice.php template
2015-03-07 19:35:13 +01:00
jekkos-t520
4ac1a943eb Update totals calculation
Update tax inclusive calculation
2015-03-06 00:58:44 +01:00
jekkos-t520
0e357f14f6 Add option to show total discounts on receipt 2015-03-05 08:28:58 +01:00
jekkos-t520
e0fd35220e Add custom search back into items
Fix proper pagintaion for filtered item views w/o search
2015-03-04 20:53:38 +01:00
jekkos-t520
ffb8501276 Fix custom search 2015-02-27 16:03:52 +01:00
jekkos-t520
8981efbd2a Fix error in database script
Add script to reset database easily (with root user)
Remove old git-svn script
2015-02-26 22:08:14 +01:00
jekkos-t520
b2f76f3fe6 Improve thai translation (Merge Pull Request #20)
Improve login screen
2015-02-26 21:55:55 +01:00
jekkos-t520
98cae8928b Cleanup table encoding + auto_increment
Conflicts:

	database/database.sql
2015-02-26 14:38:13 +01:00
jekkos-t520
b02b16b63e Only show company name in supplier's dropdown 2015-02-25 13:30:18 +01:00
jekkos-t520
81cb46bb8b Add router for sales overview 2015-02-25 13:30:18 +01:00
jekkos-t520
b11bf919af Automatically resize report columns
Remove text-transform for invoice header
2015-02-25 13:30:17 +01:00
jekkos-t520
9ee63294a4 Make report table headers resizable
Add suffix to pagination url (so extra filters can be maintained)
2015-02-25 13:30:17 +01:00
jekkos-t520
dbddb34ed4 Receipt total is recalculatd after edit now
Some css improvements for receipt printing
Adaptation of one or more labels
2015-02-25 13:30:17 +01:00
jekkos-t520
71c9294978 Giftcard customer selection cleanup 2015-02-25 13:30:17 +01:00
jekkos-t520
1226d217a6 Pagination search limit_from fixes (add limit_from hidden fields) 2015-02-25 13:30:17 +01:00
jekkos-t520
3204391521 Always show name instead of description (twice) 2015-02-25 13:30:17 +01:00
jekkos-t520
40ebba4252 Improve table layout a bit (change header sizes) 2015-02-25 13:30:17 +01:00
jekkos-t520
9351a263ee Review sales overview pagination (use generic intialisation in
secure_area.php)
2015-02-25 13:30:03 +01:00
jekkos-t520
cb1075e6c0 Remove sale_type parameter from get_detailed_sales and
get_detailed_receivings methods
2015-02-24 10:22:19 +01:00
jekkos-t520
33cf0422a7 Make javascript selector more specific 2015-02-24 09:21:03 +01:00
jekkos-t520
bd54a180d5 Remove location field from bulk edit 2015-02-24 08:55:44 +01:00
jekkos-t520
b752ec9952 Fix item bulk update 2015-02-24 08:51:09 +01:00
jekkos-t520
e688a24326 Fix sale suspend (redundant payment_type field was removed)
Gender is now properly saved in suppliers module
2015-02-24 08:10:53 +01:00
jekkos-t520
468b3e3580 Fix item_number barcode generation for item kits
Fix supplier pagination
2015-02-22 00:04:47 +01:00
jekkos-t520
e96dd7883e remove count_all
fix pagination row counts for modules
2015-02-21 23:58:31 +01:00
jekkos-t520
8e221033bf Add label for products on low inventory (items filtering)
Add stock location to method call for items pagination
Move item pagination links
2015-02-21 22:35:28 +01:00
jekkos-t520
17594f95f1 Add lines per page option
Add default sales discount option
Fix supplier pagination
2015-02-21 21:57:41 +01:00
jekkos-t520
9e427485f3 Fix items module filtering
Fix double to_currency wrapper for amount_change in receipt
Fix tablesorter js error with empty tables
Upgrade tablesorter.js to latest version
Adapt some labels for dutch
2015-02-21 18:05:12 +01:00
jekkos-t520
c389e6030c Fix pagination for all modules (links updated for index+search through
AJAX)
2015-02-20 17:36:03 +01:00
jekkos-t520
0f77300131 Sales overview added to register screen
Add pagination to default search routine
Remove payment_type column from sales table (was redundant)
Fix search spinner location (just toggle ac_loading class)
Complete sale labels
2015-02-20 15:54:13 +01:00
jekkos-t520
9856f0bc65 Add invoice dom sanity check before print 2015-02-20 09:25:31 +01:00
jekkos-t520
378d45691b invoice printer field greyed out if jsPrint addon is not installed 2015-02-20 09:22:21 +01:00
jekkos-t520
e7f9f29cd3 Create invoice checkbox was inverted 2015-02-19 18:15:35 +01:00
jekkos-t520
babddd524c Refactor variable substitution for receiving invoice numbers
Use localStorage to store selected printer
Add printer selection for invoice template
Print after sale checkbox is now present in receivings and sales screen
Remove print after sale options from receipt_config
Add default translations to receivings_lang.csv
Fix some bugs preventing to print and email at the same time
2015-02-19 18:10:31 +01:00
jekkos-t520
6ff19cb3f3 Merge branch 'feature/invoice_template' 2015-02-19 16:11:57 +01:00
jekkos-t520
42c30bf30b Invoice PDF generation working properly
PDF's can be sent as mail attachment
2015-02-19 15:32:16 +01:00
jekkos-t520
50006aeb6a Email pdf generation
Added invoice template
Add latest dompdf release
2015-02-18 18:18:09 +01:00
jekkos-t520
8edff136d0 Show company name on receipt when no logo is set 2015-02-18 08:53:18 +01:00
jekkos-t520
f6fb275dff Fix stock permission names (no more empty labels) 2015-02-18 07:42:20 +01:00
jekkos-t520
4025883765 show taxes was reversed 2015-02-18 00:16:18 +01:00
jekkos-t520
b9b8e2b4e5 remove php shorthand tags (incompatibility across installs) 2015-02-17 23:06:24 +01:00
jekkos-t520
13f6640576 Add show taxes checkbox value (state was not persisted)
Disable printing options when unchecking print after sale
2015-02-17 22:58:10 +01:00
jekkos-t520
aa969273db Disable config fields that require addon when this one is not enabled
Obey to user confirm outcome to save config or not
Fallback to window.print when addon is not installed (as it was
previously)
2015-02-17 17:08:46 +01:00
jekkos-t520
a809807288 jsPrintSetup plugin integration
All receipt print configuration options present now
2015-02-17 16:46:57 +01:00
jekkos-t520
2a07bbaef9 Merge branch 'master' into feature/receipt_config 2015-02-17 12:40:59 +01:00
jekkos-t520
4bc1d87782 Fix reports listing (listing itself was based on empty labels) 2015-02-17 12:39:32 +01:00
jekkos-t520
6710112f3d Change to bc-style operations for totals calculation in receivings 2015-02-17 12:20:53 +01:00
jekkos-t520
7ec4c21587 Merge branch 'master' into feature/receipt_config 2015-02-17 12:20:33 +01:00
jekkos-t520
9b61e0ad67 Add proper location id for 0 quantities 2015-02-17 11:14:28 +01:00
jekkos-t520
40eef74aa0 Adapt load_config hook to fallback to english if no valid language is
present
Move CI system language files back to their correct location
(form_validation_lang)
Add upload_lang.php and email_lang.php to system files (default english
version for the time being)
Add account_number check when importing customer data from excel
Avoid unnecessary calls to check_item_number when importing items from
excel
2015-02-17 11:06:28 +01:00
jekkos-t520
c7c4ac15d6 Set update_row response datatype to html
Remove MY_Validation override (not needed anymore)
Add jquery validation check for account_number
2015-02-17 09:03:47 +01:00
jekkos-t520
cf2ca1e6c7 Start receipt rework 2015-02-17 09:01:56 +01:00
jekkos-t520
9d0fb43fb1 add file extension to company logo filename 2015-02-15 12:53:56 +01:00
jekkos-t520
99c37ad9a7 Show real giftcard remainder on receipt (fix) 2015-02-12 15:51:46 +01:00
jekkos-t520
a8e15f7db0 Add company logo upload feature in config (will show on receipts) 2015-02-12 15:47:31 +01:00
jekkos-t520
b5c33b4fcb Add $YCO parameter (invoice numbering per year) 2015-02-12 15:19:57 +01:00
jekkos-t520
9e9a9a80a9 Fix tax percentage calculations (issue #42)
Use bcadd calculations for payment totals
Add tax inclusive option back into general_config
2015-02-12 14:57:39 +01:00
jekkos-t520
fe081779c5 Fix number_format bug when entering an empty payment amount
Add giftcard balance to final receipt (if giftcard payment present)
2015-02-12 14:34:08 +01:00
jekkos-t520
1267e92117 Fix sale and receiving return (incorrect receipt numbers were generated)
Fix item form width in receivings
2015-02-12 10:47:09 +01:00
jekkos-t520
d6f27141af Add back create invoice label 2015-02-11 17:25:45 +01:00
jekkos-t520
4b48a97166 Remove duplicated labels after merge
Add accidentally removed language lines
2015-02-11 16:42:28 +01:00
jekkos-t520
ed70fdc40b Change female language in english to 'F' 2015-02-11 16:33:28 +01:00
jekkos-t520
ade913f0e4 Merge github pull request #30 2015-02-11 16:31:31 +01:00
jekkos-t520
cd92f85691 Merge branch 'feature/customer_gender' 2015-02-11 14:30:15 +01:00
jekkos-t520
081bf77782 Fix module hotkeys 2015-02-11 14:29:57 +01:00
jekkos-t520
4e6a6d6434 Add gender to people + employees form 2015-02-11 14:29:46 +01:00
jekkos-t520
57cbbdab56 Remove content-length header for download
Clean headers (if already output) before force_download
2015-02-11 14:00:27 +01:00
jekkos-t520
4a6a69bdef Enable image headers again 2015-02-11 13:53:21 +01:00
jekkos-t520
88a9193d3a Remove escape characters from receivings_lang
Adapt confirmation dialog text for logout
2015-02-11 13:50:21 +01:00
jekkos-t520
4b99252a83 Change font-family globally to fixed order Arial-sans-serif-...
Remove second submit button on general_config page
2015-02-11 13:47:20 +01:00
jekkos-t520
8b1186c4f9 Fix barcode generation for item kits 2015-02-11 13:31:08 +01:00
jekkos-t520
2e36b7e03b Fix undefined offset error when using company_name with trailing
whitespace
2015-02-11 13:14:33 +01:00
jekkos-t520
b7751bde1a Fix serialized search filter in items module 2015-02-11 12:19:32 +01:00
jekkos-t520
f021215cbb Show proper error message on upload misconfiguration
Use jquery-validation to do item_number check
Fix invoice_number validation in sale edit form
2015-02-11 11:32:58 +01:00
jekkos-t520
9191eb4535 Show error messages in item form when upload fails
Remove check for validation flag in handle_server_validation
2015-02-11 08:53:16 +01:00
jekkos-t520
0fdb760d6f Add specific mime type for backup download 2015-02-10 21:20:10 +01:00
jekkos-t520
a46221ad04 Change compression method to zip again (retry) 2015-02-10 18:32:17 +01:00
jekkos-t520
da5c65e42e Fix location config bug (add check for disallowed characters)
Add labels for different languages (just default to english for now)
2015-02-10 16:34:16 +01:00
jekkos-t520
3594c348ee Fix stock location permissions for github issue #34 2015-02-10 16:04:52 +01:00
jekkos-t520
c40ad31ed0 Review item import (check item_number and throw proper error) 2015-02-10 15:43:10 +01:00
jekkos-t520
4579f16518 Add empty uploads/item_pics directories
Add .gitignore files to ignore uploaded item pics (or saved db scripts)
2015-02-10 11:17:46 +01:00
jekkos-t520
49a0a4e127 download backpus as plain sql file 2015-02-10 11:00:09 +01:00
jekkos-t520
d208e6dccb set menubar font sizes to px instead of pt
add item_number duplication check when adding new items
fix barcode on receivings receipt (after completion)
change backup db download format to gz
2015-02-10 10:56:37 +01:00
jekkos-t520
8b2a2f8074 Fix sale_id generation for sale receipt 2015-02-10 00:31:53 +01:00
jekkos-t520
84ffe5d4eb Add try catch to barcode generation (so errors are displayed in case of
config problems)
2015-02-10 00:05:42 +01:00
jekkos-t520
39301e7091 Fix receipts after sale and receiving completion 2015-02-09 23:37:19 +01:00
jekkos-t520
0a29809f7a Add item code as barcode label 2015-02-09 19:56:16 +01:00
jekkos-t520
4332bce401 Replace old code39 generator for receipts with new barcode_lib
functionality
2015-02-09 18:08:46 +01:00
jekkos-t520
42f4eb9c7b Add code128 as barcode_type 2015-02-09 17:32:45 +01:00
jekkos-t520
bd5e5b0b94 Remove item code as barcode parameter
Always show barcode itself as a second row
2015-02-09 17:03:52 +01:00
jekkos-t520
c763e8831f Increase width config tab
Increase width item kits form
Add ob_clean call to before force_download
2015-02-09 16:57:44 +01:00
jekkos-t520
0616560e3e Change barcode width/height constraints (set to minimum 10 for height
and 60 for width)
2015-02-09 09:47:01 +01:00
jekkos-t520
c7a898a89d Location autocomplete zipcode/city will fallback on different fields
when defaults are empty
Try to fix default text removal in register's customer/item selection fields
2015-02-09 08:51:00 +01:00
jekkos-t520
377284f491 Add company name to barcode layout options 2015-02-08 18:05:29 +01:00
jekkos-t520
93968814db Add sales and receivings format in general config 2015-02-08 17:51:03 +01:00
jekkos-t520
941ff4cd4a Upgrade jquery form to 3.51
Make item_number field non required
Save item_number properly
2015-02-07 20:22:21 +01:00
jekkos-t520
4d2de519a1 Set explicit version number for jquery-validation (1.13.1) 2015-02-06 17:34:37 +01:00
jekkos-t520
8fd315e389 Fix error message styling + add UPC required error message in items_lang 2015-02-06 17:28:49 +01:00
jekkos-t520
fe786b5888 Fix enable invoice labels (sales and receivings) 2015-02-06 17:06:31 +01:00
jekkos-t520
28711df45a Merge branch 'feature/item_pics'
Conflicts:
	application/helpers/table_helper.php
2015-02-06 16:55:09 +01:00
jekkos-t520
f77f2e6e29 item pics are now resized and cached server side 2015-02-06 16:53:37 +01:00
jekkos-t520
707114805d Fix autocomplete problem with new item in sales module 2015-02-06 15:48:57 +01:00
jekkos-t520
aebed9347b Merge branch 'feature/address_completion'
Conflicts:
	application/controllers/items.php
2015-02-06 15:01:54 +01:00
jekkos-t520
aa1c46738b Country code is now resolved using ipinfo.io api call
Address autocompletion mostly works now
2015-02-06 14:59:11 +01:00
jekkos-t520
080ae5df3a Upgrade jQuery to 1.8.3
Upgrade jQuery Validation to 1.3.2
Add jQuery migrate (for developmet purpose)
2015-02-06 14:58:22 +01:00
jekkos-t520
c04fa362b1 Add database backup feature in config 2015-02-06 11:22:03 +01:00
jekkos-t520
c7280e6018 Minor code review 2015-02-05 17:26:27 +01:00
jekkos-t520
374f9556d1 Don't try to preview images when there are none 2015-02-05 17:25:35 +01:00
jekkos-t520
ec6455647f Merge branch 'feature/item_pics'
Conflicts:
	database/2.3_to_2.3.1.sql
	database/database.sql
	translations/items_lang.csv
2015-02-05 17:05:51 +01:00
jekkos-t520
7081fe0338 Remove non free barcodegen library
Add width and height parameters for code39 barcode generation
2015-02-05 17:00:41 +01:00
jekkos-t520
8b6182dded Further fine tuning address autocompletion 2015-02-05 15:26:37 +01:00
jekkos-t520
f2fd360d23 Minimal working OpenStreetMap autocomplete
Refactor jkey F-key bindings
2015-02-05 14:08:40 +01:00
jekkos-t520
8ccba57d98 Images are not removed from items when no file is chosen
Image thumbnails are resized proportionally and updated after update_row
refresh
Add some padding to the thumbnails
Move select specific styles to ospos.css stylesheet
2015-02-04 15:29:34 +01:00
jekkos-t520
7efb1428f5 Basic image upload working now 2015-02-04 13:27:09 +01:00
jekkos-t520
46cfe0b18a Add databse upgrade script for 2.3.2 2015-02-03 17:33:43 +01:00
jekkos-t520
2f74c400bd Fix receipt barcodes (revert to the old script) 2015-02-03 15:08:21 +01:00
jekkos-t520
7c24682584 Fix stock location removal 2015-02-02 16:55:26 +01:00
jekkos-t520
215a2104a1 Add item_id/upc barcode content option
Fix tabcontent width
Improve location config screen
2015-02-02 15:48:41 +01:00
jekkos-t520
aae02b7420 Move location config to separate tab
Fix javascript error message display in barcode and location config
2015-02-02 14:46:05 +01:00
jekkos-t520
48867d44be Remove barcode.png
Set correct permissions on Arial.ttf
2015-02-02 13:24:26 +01:00
jekkos-t520
d3b9bc08c9 Merge branch 'master' into feature/barcode_patch 2015-02-02 12:54:04 +01:00
jekkos-t520
1bf839601a Barcode patch applied (using barcodegen) 2015-02-02 12:44:58 +01:00
jekkos-t520
bd763596b9 Bump version to 2.3.2 2015-02-01 14:48:25 +01:00
jekkos
dc12c7bdd2 Merge pull request #29 from marteserede/master
Two small edits (#27 and #28)
2015-01-30 08:01:22 +01:00
Martes Erede
4ddec66d7b #27 Using bc-style operations now. 2015-01-29 16:22:44 +01:00
Martes Erede
839849bf8d Merge branch 'master' of https://github.com/jekkos/opensourcepos 2015-01-29 16:16:55 +01:00
Martes Erede
b4ac74aa55 Bugfix #28 if invoice number is empty, set it to null, so no error occurs 2015-01-29 16:14:12 +01:00
jekkos-t520
8ac58a4612 Merge branch 'master' of https://github.com/jekkos/opensourcepos 2015-01-28 19:25:37 +01:00
jekkos-t520
08325c8cf7 Fix last stock_locations config quirks 2015-01-28 19:25:18 +01:00
jekkos
43f4e9e790 Update README.md
add bitcoin donation address to readme
2015-01-27 23:40:50 +01:00
jekkos-t520
f497ac54f1 Review stock location management in config screen (merged old refactor
branch)
Remove some obsolete labels from tranlations csv's
2015-01-27 23:27:43 +01:00
jekkos-t520
f323bec9ca Merge branch 'refactor/edit_stock_locations'
Conflicts:
	application/controllers/config.php
	application/models/stock_locations.php
2015-01-27 19:24:59 +01:00
jekkos-t520
24a6c9c34f Config location editing works now 2015-01-27 19:03:57 +01:00
jekkos-t520
4f456c75f7 Merge branch 'master' of https://github.com/jekkos/opensourcepos 2015-01-26 08:13:55 +01:00
jekkos-t520
8705b91bd3 Start refactoring the stock locations configuration screen 2015-01-26 08:11:21 +01:00
jekkos
16814f2d12 Update README.md 2015-01-25 16:39:31 +01:00
jekkos-t520
b9ee7b6b01 Invert quantity correction when deleting sales 2015-01-25 16:07:53 +01:00
jekkos-t520
344cb5c749 Add route for no_access controller with just one parameter
Remove permission dependency between sales, receivings and items module
Add explicit stock permission to sales and receiving modules
2015-01-25 15:46:42 +01:00
jekkos-t520
4556938154 Add cookie cleanup after search() and get_row() 2015-01-25 14:32:15 +01:00
jekkos-t520
a2b7ff688d Merge branch 'master' of https://github.com/jekkos/opensourcepos 2015-01-25 14:13:38 +01:00
jekkos
df71cb9cf8 Merge pull request #26 from marteserede/master
Fix for bug #39, error in excel_import
2015-01-25 00:22:26 +01:00
Martes Erede
927dbbabb6 Feature #27: Changing cost price while receiving items 2015-01-24 21:40:07 +01:00
Malte Srocke
7544aae394 Bug #25: Deleting a sale/receiving would not effect the stock/quantity. 2015-01-24 19:44:19 +01:00
jekkos-t520
868721c6f5 Adapt sql scripts for new barcode label options 2015-01-24 15:18:00 +01:00
jekkos-t520
1d9661d0b0 Add name, price and company name as possible barcode labels 2015-01-24 15:06:29 +01:00
jekkos-t520
e7fd57afdf Cleanup duplicate cookies when browsing items module 2015-01-23 00:18:43 +01:00
jekkos-t520
ad51278ba5 UPC config option now adds UPC content to barcodes as well 2015-01-22 23:36:56 +01:00
Malte Srocke
606eb01a67 - fixed excel import: if there was missing a location-id in the import-csv, then there would not have been created an entry into item_quantities for the item and location.
- fixed also the inventory info-part: before in the inventory-table there was no quantity inserted and data[10] as the location_id
2015-01-22 22:19:23 +01:00
jekkos-t520
94287ddec4 Make barcode text configurable 2015-01-21 13:14:16 +01:00
jekkos-t520
3d6d473145 Add item field autofocus in sales module
auto submit payment form when hitting enter in amount_tendered input
field
2015-01-19 12:35:09 +01:00
jekkos-t520
565a143c71 Merge branch 'master' of https://github.com/jekkos/opensourcepos 2015-01-19 12:07:40 +01:00
jekkos-t520
c959d97fd1 Fix for bug #39: item with 0 stock is not listed in item-list 2015-01-19 12:02:53 +01:00
jekkos
62c122e7ee Update WHATS_NEW.txt 2015-01-19 12:01:58 +01:00
jekkos
f3b3904b43 Merge pull request #23 from marteserede/master
Fixing session-table-name bug
2015-01-19 11:56:42 +01:00
jekkos-t520
883237a41c Regenerate language files (obsolete labels) 2015-01-16 18:43:39 +01:00
jekkos-t520
dd369118c5 Add check payment type to temp sales table again 2015-01-16 18:43:38 +01:00
jekkos-t520
b0aa62b044 Add turkish language (patch #24 Turkish Language) 2015-01-16 18:29:57 +01:00
jekkos-t520
84a141d9c5 Move translations into CSV files
Add generate_languages.php script (run using `php
generate_languages.php`)
Add generated language files
2015-01-16 17:40:41 +01:00
Malte Srocke
acb764a040 Changing config-sess_table_name value so no error occurs if dbprefix is changed to something else than "ospos_" 2015-01-14 23:34:43 +01:00
jekkos
37be5c9446 Merge pull request #17 from songwutk/patch-5
Update config_lang.php
2015-01-12 09:02:22 +01:00
jekkos
cd8d9a4a3d Merge pull request #16 from flodef/patch-4
Patch 4
2015-01-12 09:01:48 +01:00
jekkos
b1baa5609a Merge pull request #19 from songwutk/patch-7
Update items_lang.php
2015-01-12 09:00:53 +01:00
jekkos
614b4d48b6 Merge pull request #18 from songwutk/patch-6
Update module_lang.php (Thai)
2015-01-12 09:00:18 +01:00
songwutk
0b98050ea1 Update items_lang.php
Update Thai Translate
2015-01-12 12:36:24 +07:00
songwutk
3e0a6d9ba6 Update module_lang.php
Fix duplicate variable
2015-01-12 12:25:45 +07:00
songwutk
94c4a0c909 Update config_lang.php
Update more translate config from English.
2015-01-12 12:23:56 +07:00
Florian
ae454026dc Update upload_lang.php 2015-01-10 18:43:12 +01:00
Florian
e9e6290151 Update unit_test_lang.php 2015-01-10 18:42:41 +01:00
Florian
d7ed07f4f4 Update profiler_lang.php 2015-01-10 18:39:51 +01:00
Florian
ed3234ff30 Update number_lang.php 2015-01-10 18:39:22 +01:00
Florian
1ff7e271a5 Update migration_lang.php 2015-01-10 18:37:38 +01:00
Florian
5e4a6af3b7 Update imglib_lang.php 2015-01-10 18:37:01 +01:00
Florian
34d8ef7637 Update ftp_lang.php 2015-01-10 18:36:39 +01:00
Florian
cb760d9ed8 Update form_validation_lang.php 2015-01-10 18:36:08 +01:00
Florian
5af96fb082 Update email_lang.php 2015-01-10 18:35:32 +01:00
Florian
248c47d94a Update db_lang.php 2015-01-10 18:35:04 +01:00
Florian
1ee5b66a45 Update date_lang.php 2015-01-10 18:34:28 +01:00
Florian
fc0e691569 Create calendar_lang.php 2015-01-10 18:33:17 +01:00
jekkos
2ad00de947 Update 2.3_to_2.3.1.sql
[opensourcepos:bugs] #38 remove hardcoded reference to database name
2015-01-09 15:57:45 +01:00
Florian
6445c463a4 Create upload_lang.php
Didn't translate this one yet, but will finish the job later ...
2014-12-31 15:09:09 +01:00
Florian
1b8d40d7ed Create unit_test_lang.php
Translate unit_test_lang.php to French.
2014-12-31 15:08:33 +01:00
Florian
4d6a5189c1 Create profiler_lang.php
Translate profiler_lang.php to French.
2014-12-31 15:05:43 +01:00
Florian
326b074a22 Create number_lang.php
Translate number_lang.php to French
2014-12-31 14:59:46 +01:00
Florian
ccbe227666 Create migration_lang.php
Didn't translate this one yet, but will finish the job later ...
2014-12-31 14:58:25 +01:00
Florian
228b99ebbf Create index.html
Translate index.html to French
2014-12-31 14:57:48 +01:00
Florian
9df3236176 Create imglib_lang.php
Didn't translate this one yet, but will finish the job later ...
2014-12-31 14:56:21 +01:00
Florian
546b94f1e9 Create ftp_lang.php
Didn't translate this one yet, but will finish the job later ...
2014-12-31 14:55:55 +01:00
Florian
dbec337742 Create form_validation_lang.php
Translate form_validation_lang.php to French
2014-12-31 14:55:24 +01:00
Florian
e387ddaf1a Create email_lang.php
Didn't translate this one yet, but will finish the job later ...
2014-12-31 14:45:23 +01:00
Florian
2fc34a55b1 Create db_lang.php
Didn't translate this one yet, but will finish the job later ...
2014-12-31 14:44:43 +01:00
Florian
17b0f452ca Create date_lang.php
Translate date_lang.php to French
2014-12-31 14:43:12 +01:00
jekkos-t520
0f34a6ace4 Ignore receiving_quantity = 0 for receivings 2014-12-26 11:39:26 +01:00
jekkos-t520
58dc5e3062 Newly created item in sales module is auto added to sale again 2014-12-19 08:47:40 +01:00
jekkos-t520
c00c604c21 Fix tax inclusive pricing for non taxable customer
Show receiving quantity in receiving receipt
Allow to use invoice number as zero (a bug prevented 0 to be saved to
db, causing it to stop incrementing next time)
2014-12-18 19:55:20 +01:00
jekkos-t520
0132c9092c Remove clear enable functions 2014-12-16 19:00:15 +01:00
jekkos-t520
efff9d20c1 Add giftcard number autogeneration 2014-12-16 18:36:26 +01:00
jekkos-t520
07a32a0deb Remove invoice / sale format enable option
Add giftcards -> person_id foreign key constraint
Fix tax calculation for non taxable customers
Improve giftcard person autocompletion
2014-12-16 08:49:49 +01:00
jekkos-t520
b546883d2a Dashes are allowed now in barcode text
Update barcode with newer drop in version (thanks Odie)
2014-12-15 00:09:44 +01:00
jekkos-t520
0569275c4e Add receivings invoice number enable/disable
Ignore invoice number unique constraint when invoice is disabled
2014-12-06 10:46:16 +01:00
jekkos-t520
e3288ae759 Revert newly added receivings functionality 2014-12-04 23:23:10 +01:00
jekkos-t520
64f1b4191a Invoice number can now be enabled/disabled
Invoice unique constraint only applies when enabling invoice
Correct several HTML structure errors in sale and order modules
Make the top module bar a bit wider
Fix payment details css when hiding/showing invoice number field
2014-12-04 22:01:40 +01:00
jekkos-t520
1c1b89db21 Fix detailed sales report for sale_type = sale 2014-12-04 08:49:36 +01:00
jekkos-t520
ee30b178b4 Remove duplicate statement from 2.3.1 sql upgrade script
Remove quantity column from database.sql
Update item import example with correct location id
Fix detailed report receipt links
2014-11-28 08:51:08 +01:00
jekkos-t520
44b5404fe7 Fix missing invoice number in sales receipt 2014-11-27 08:48:15 +01:00
jekkos-t520
78ca94e036 Fix detailed report editing 2014-11-27 08:44:50 +01:00
jekkos-t520
d7a61080e2 Add duplicate invoice number error message in sales module
Fix colspan width for receivings (one column was added for
receiving_quantity)
2014-11-26 21:56:18 +01:00
jekkos
27e1bfe871 Merge pull request #11 from flodef/master
Add record time to giftcard
2014-11-26 08:55:49 +01:00
jekkos
7d70fa70a6 Merge pull request #13 from flodef/patch-3
Update register.php
2014-11-26 08:54:44 +01:00
jekkos-t520
6bd6080ccc Fix customer field focus in register screen 2014-11-26 08:25:45 +01:00
jekkos-t520
b311eca62e Fix receiving_quantity error in receivings page 2014-11-26 08:17:43 +01:00
jekkos-t520
99a804c1ac Fix item quantity after edit 2014-11-26 08:10:15 +01:00
jekkos-t520
5f3a81ee5c Merge branch 'master' of https://github.com/jekkos/opensourcepos 2014-11-25 23:04:25 +01:00
jekkos-t520
af8b88d030 Fix config module layout (wider + adapt setting labels) 2014-11-25 21:45:01 +01:00
Florian
08368b6279 Update register.php
Correct an HTML mistake : replace </tr> by </td>
2014-11-25 20:24:46 +01:00
jekkos-t520
97d37b5ad9 Merge branch 'feature/sale_numbering'
Conflicts:
	application/libraries/Receiving_lib.php
	database/2.3_to_2.3.1.sql
	js/manage_tables.js
2014-11-25 17:20:05 +01:00
jekkos
969abe1d34 Merge pull request #7 from flodef/patch-1
Update Sale_lib.php
2014-11-25 11:35:07 +01:00
jekkos-t520
5a40c31af3 Merge branch 'fix/recv_amount_multiplication' 2014-11-25 08:43:10 +01:00
jekkos-t520
62977bf89f Refresh only editd rows after bulk refresh 2014-11-25 08:32:55 +01:00
jekkos
f88baad4b7 Merge pull request #3 from songwutk/patch-2
fix bug duplicate config
2014-11-25 00:11:26 +01:00
Florian
27de99fe78 Update giftcards.php
Add a "record_time" to the giftcard saving function to be know when a new giftcard has been added.
2014-11-24 20:11:47 +01:00
Florian
44515ee999 Update database.sql 2014-11-24 20:06:44 +01:00
jekkos
90c1934d92 Merge pull request #6 from curthauta/import_items
fixed quantities on items import
2014-11-20 10:19:41 +01:00
jekkos
bb3fbb03b1 Merge pull request #9 from flodef/patch-2
Update register.php
2014-11-20 10:17:39 +01:00
jekkos
d6926fee9f Merge pull request #10 from flodef/patch-3
Update register.php
2014-11-20 10:15:49 +01:00
flodef
ba47d5e4ff Update register.php
- Correct previous commit when it was impossible to set any value anymore (oooops, I went too quickly on this one)
- Use .val instead of .attr("value") because the latter is deprecated
2014-11-14 22:40:36 +01:00
flodef
fe14180ec5 Update register.php
- Remove unused variable : $cur_item_info
- Change .click for .focus : correct a bug when the text doesn't disappear on chrome after changing tab
2014-11-14 22:30:16 +01:00
flodef
15d15b9750 Update register.php
Price should have it's currency
2014-11-14 20:31:04 +01:00
jekkos-t520
46af17845f Merge branch 'master' of https://github.com/jekkos/opensourcepos 2014-11-13 16:51:09 +01:00
jekkos-t520
2c706dc752 Rename receivings_quantity to receiving_quantity 2014-11-13 16:50:52 +01:00
jekkos-t520
4893da0e43 Added enable invoice checkbox 2014-11-13 13:28:42 +01:00
flodef
26bee3e5a6 Update Sale_lib.php
Clean "add_payment" to be simpler : don't need to declare $payment in the main as it used only to add to an existing array
Fix "edit_payment" that was trying to set a payment on the $payment_id (expected payment data : $payments)
2014-11-11 13:04:27 +01:00
jekkos-t520
1b41c93441 Fix item row refresh in detailed receivings and sales 2014-11-07 18:03:45 +01:00
jekkos
1dd9a7ea2c Merge pull request #4 from songwutk/patch-3
Update Thai translate
2014-11-06 08:52:27 +01:00
jekkos
6286f6cc11 Merge pull request #2 from songwutk/patch-1
Rename Thai menu (Gift Card)
2014-11-06 08:14:11 +01:00
jekkos-t520
094a293c78 Fix invoice number insert in receivings 2014-11-06 07:35:54 +01:00
Curt Hauta
8ae21bbcb7 fixed quantities on items import 2014-11-05 20:01:25 -07:00
jekkos-t520
035f2ad946 Review receivings quantity mulitplier 2014-11-04 08:14:16 +01:00
jekkos-t520
54fe0c4c30 Fix sales/receivings deletion 2014-11-03 19:22:36 +01:00
jekkos-t520
d43015e8c9 Set default receivings value for items to 1 in database
Fix item quantity save
2014-11-03 18:08:57 +01:00
jekkos-t520
7a46078734 Add receivings_quantity to item form
Conflicts:

	application/libraries/Receiving_lib.php
	database/2.3_to_2.3.1.sql
2014-11-03 18:07:50 +01:00
jekkos-t520
3007173833 Add invoice numbering to sales module 2014-10-31 07:58:46 +01:00
jekkos-t520
96c349b099 Add invoice numbering to sales module 2014-10-30 19:22:49 +01:00
songwutk
e5e74f3ffb Update Thai translate
Update Thai translate
2014-10-26 06:37:53 +07:00
songwutk
851333b72c fix bug duplicate config
fix bug duplicate config & update thai translate
2014-10-26 06:29:07 +07:00
songwutk
00045c3f97 Rename Thai menu (Gift Card) 2014-10-26 06:14:28 +07:00
jekkos-t520
cc9adeef47 Set supplier_info to default value (fix warning with empty supplier)
Conflicts:

	application/controllers/receivings.php
2014-10-23 21:32:19 +02:00
jekkos-t520
37c80deb83 Show error message correctly in receivings 2014-10-23 18:35:34 +02:00
jekkos-t520
9d765efc94 Add invoice number duplication check in receivings screen
Add invoice number duplication check in receivings detailed report
2014-10-23 18:02:33 +02:00
jekkos-t520
4e071c9fbb Fix blank page when returning receivings
Conflicts:

	application/libraries/Receiving_lib.php
2014-10-22 07:11:37 +02:00
jekkos-t520
fe6820920b Add edit form in detailed receivings report
Add language lines for detailed sale & receiving validation
Add permission for getting updated sale & receiving detail row
2014-10-21 21:39:22 +02:00
jekkos-t520
08dcd9e384 Fix totals calculation in receivings module 2014-10-20 18:31:33 +02:00
jekkos-t520
31877528b1 Security update for report subpermissions 2014-10-17 08:29:46 +02:00
jekkos-t520
7fad710277 Reports module now has checks on the required submodules when entering direct urls 2014-10-16 18:31:35 +02:00
jekkos-t520
7bcb1d7531 Fix totals calculation in sales register 2014-10-16 08:23:47 +02:00
jekkos-t520
0863b4ebba Add extra permissiosn checks to report methods
Adapt no_access page to show missing grant
Adapt Email library to customize return_path header
2014-10-16 08:20:36 +02:00
jekkos-t520
20fed1d457 Update database script to latest version again 2014-10-13 19:09:42 +02:00
jekkos-t520
bc514889a8 Add detailed reports again (permission_id wasn't set properly)
Finish updqate script
2014-10-13 18:57:07 +02:00
jekkos-t520
868de15fcb Invoice number is now substituted correctly after complete
Fix payments summary report
Update upgrade script (not tested yet)
2014-10-13 12:30:37 +02:00
jekkos-t520
dad16b7a71 Receivings comments and recv_invoice_format fields are updated on keyup now 2014-10-12 12:47:45 +02:00
jekkos-t520
de06b64e4e Fix warning in specific discount report (add category to query)
Add grant for default stock location in sql
2014-10-12 03:18:19 +02:00
jekkos-t520
ac667a23dc Refactor report listing (again)
All reports have proper permissions now (categories, taxes, discounts,..)
Minor refactoring and code cleanup
2014-10-12 02:54:54 +02:00
jekkos-t520
54fbb4e7d7 Refactor stock location save
Add config to permissions and grants in database script
2014-10-11 17:55:00 +02:00
jekkos-t520
cda41622e4 Merge branch 'refactor/module_permissions' into feature/invoice_numbering
Conflicts:
	database/database.sql
2014-10-10 16:38:07 +02:00
jekkos-t520
802a0e6a59 Refactor report listing (move sales specific grants to employee model) 2014-10-09 17:56:24 +02:00
jekkos-t520
77222c1989 Fix supplier selection 2014-10-08 19:27:07 +02:00
jekkos-t520
bb9ab9bd9c Finish stock location refactoring
Modules are inaccessible if permissions not configured correctly
2014-10-07 21:47:07 +02:00
jekkos-t520
a1eb735d96 Remove empty check for item_id and quantity_id when saving item_quantities
Add configuration key to database script
2014-10-07 08:27:31 +02:00
jekkos-t520
aee5f3d27e Add ospos_grants table
Refactor permission and module system
2014-10-07 00:47:30 +02:00
jekkos-t520
97ff190ba5 Receivings can now use invoice numbers with a specified format (in config section)
Fix some more stock location related bugs
Some refactoring todo for creating/updating stock locations
2014-10-05 22:53:42 +02:00
jekkos-t520
1afbc03304 Merge branch 'feature/multistore_support' into feature/invoice_numbering 2014-10-04 16:49:22 +02:00
jekkos-t520
5e8367681b Fix default location in items, sales and receivings 2014-10-04 13:38:52 +02:00
jekkos-t520
bac80e3a92 Added invoice_number field to receivings table
Save comments field in receivings as in sales
Add translations for cancel button in receivings
Add cascading deletes for ospos_permissions
2014-10-04 13:19:24 +02:00
jekkos-t520
5a4ac8dde3 Fix comments in receivings 2014-10-03 17:41:39 +02:00
jekkos-t520
e994cd5444 Fix default location selected (now uses allowed locations only) 2014-10-03 10:33:34 +02:00
jekkos-t520
d486202c4f Merge branch 'feature/multistore_support' into feature/invoice_numbering 2014-10-03 08:03:59 +02:00
jekkos-t520
298135dabc Remove obsolete item_unit method 2014-10-03 08:03:37 +02:00
jekkos-t520
eb51b680aa Show stock location names in employee form 2014-10-03 00:27:50 +02:00
jekkos-t520
48acd204f1 Show stock location names in employee form 2014-10-02 23:26:29 +02:00
jekkos-t520
2929dfe9dd Add stock location filtering on items 2014-10-02 19:33:35 +02:00
jekkos-t520
290533612e Fix submodules in header 2014-09-29 21:06:06 +02:00
jekkos-t520
13127c4dc7 Deny access to report submodules on which employee doesn't have permissions 2014-09-24 08:47:00 +02:00
jekkos-t520
5cf73130c5 Modules are not accessible if employee has no permissions on at least one of it's submodules
Some more db script modifications
2014-09-23 19:14:25 +02:00
jekkos-t520
4b16b68f24 Added more granular permissions for report module
Extend permission system to allow 'submodule' permissions
Add permissions for stock locations (multistore support)
2014-09-22 20:41:53 +02:00
jekkos-t520
e03611080e Merge branch 'feature/tax_included_pricing' 2014-09-15 21:27:33 +02:00
jekkos-t520
ff7b305058 Added tax inclusive pricing to sales and receivings 2014-09-15 21:27:11 +02:00
jekkos-t520
0d76974e69 Discount field in receiving made editable again 2014-09-15 20:18:24 +02:00
jekkos-t520
87c242b35f Discount field in receiving made editable again 2014-09-15 20:14:37 +02:00
jekkos-t520
e43f7ac7bb Add deleted checkbox filter in items page (was remove due to wrong merge) 2014-09-10 19:03:19 +02:00
jekkos
c9c389e9f3 Update README and WHATS NEW
git-svn-id: svn+ssh://svn.code.sf.net/p/opensourcepos/code/@132 c3eb156b-1dc0-44e1-88ae-e38439141b53
2014-09-10 18:58:56 +02:00
941 changed files with 277491 additions and 40266 deletions

3
.gitattributes vendored Normal file
View File

@@ -0,0 +1,3 @@
dist/ merge=ours
application/language/**/*.php merge=ours
text=auto

12
.gitignore vendored
View File

@@ -1,13 +1,21 @@
node_modules
bower_components
application/config/email.php
application/config/database.php
*.patch
patches/
.idea/
git-svn-diff.py
*.bash
*.sh
*.swp
.swp
.buildpath
.project
.settings/*
*.swp
*.rej
*.orig
*~
*.~
*.log
dist/*_bower.*
application/sessions/*

50
.htaccess Executable file
View File

@@ -0,0 +1,50 @@
RewriteEngine On
# Option 2: To rewrite "domain.com -> www.domain.com" uncomment the following lines.
# RewriteCond %{HTTPS} !=on
# RewriteCond %{HTTP_HOST} !^www\..+$ [NC]
# RewriteCond %{HTTP_HOST} (.+)$ [NC]
# RewriteRule ^(.*)$ http://www.%1/$1 [R=301,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?/$1 [L]
# disable directory browsing
# For security reasons, Option all cannot be overridden.
#Options All -Indexes
Options +ExecCGI +Includes +IncludesNOEXEC +SymLinksIfOwnerMatch -Indexes
# prevent folder listing
IndexIgnore *
# secure htaccess file
<Files .htaccess>
order allow,deny
deny from all
</Files>
# prevent access to PHP error log
<Files error_log>
order allow,deny
deny from all
satisfy All
</Files>
# prevent access to generate_languages.php
<Files generate_languages.php>
order deny,allow
deny from all
allow from 127.0.0.1
# My IP(s)
# allow from xxx.xxx.xxx.xx
</Files>
<IfModule mod_expires.c>
<FilesMatch "\.(jpe?g|png|gif|js|css)$">
ExpiresActive On
ExpiresDefault "access plus 1 week"
</FilesMatch>
</IfModule>

16
.travis.yml Normal file
View File

@@ -0,0 +1,16 @@
sudo: false
language: node_js
node_js:
- "4.1"
services:
- docker
before_install:
- docker build -t jekkos/opensourcepos .
- docker run -d jekkos/opensourcepos
script:
- docker exec -t -i $(docker ps | tail -n 1 | cut -d' ' -f1) /bin/sh -c "while ! curl http://localhost/index.php | grep username; do sleep 1; done; cd app && grunt mochaWebdriver:test"

View File

@@ -1,6 +1,14 @@
The MIT License (MIT)
Copyright (c) 2014 pappastech
Copyright (c) 2012-2014 pappastech
Copyright (c) 2012 Alain
Copyright (c) 2013 Rob Garrison
Copyright (c) 2013 Parq
Copyright (c) 2013 Ramel
Copyright (c) 2014-2016 jekkos
Copyright (c) 2015-2016 FrancescoUK (aka daN4cat)
Copyright (c) 2015 Aamir Shahzad (aka asakpke), RoshanTech.com
Copyright (c) 2015 Toni Haryanto (aka yllumi)
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
@@ -17,4 +25,4 @@ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

27
Dockerfile Normal file
View File

@@ -0,0 +1,27 @@
FROM ubuntu:utopic
MAINTAINER jekkos
RUN sed -i -e 's/archive.ubuntu.com\|security.ubuntu.com/old-releases.ubuntu.com/g' /etc/apt/sources.list
RUN apt-get update
RUN apt-get -y upgrade
RUN DEBIAN_FRONTEND=noninteractive apt-get -y install mysql-client mysql-server apache2 libapache2-mod-php5 pwgen python-setuptools vim-tiny php5-mysql php5-gd php5-apcu nodejs npm curl
RUN easy_install supervisor
ADD ./docker/foreground.sh /etc/apache2/foreground.sh
ADD ./docker/supervisord.conf /etc/supervisord.conf
RUN chmod 755 /etc/apache2/foreground.sh
# Install dependencies
RUN apt-get install -y --no-install-recommends software-properties-common
RUN apt-get install -y python git
# Get latest Ospos source from Git
RUN git clone https://github.com/jekkos/opensourcepos.git /app
# RUN cd app && git checkout develop/2.4
RUN ln -s /usr/bin/nodejs /usr/bin/node
RUN cd app && npm install
RUN npm install -g grunt-cli
RUN ln -s /usr/local/bin/grunt /usr/bin/grunt
RUN ln -fs /app/* /var/www/html
ADD ./docker/start_container.sh /start_container.sh
RUN chmod 755 /start_container.sh
EXPOSE 80 3306
CMD ["/bin/bash", "/start_container.sh"]

116
Gruntfile.js Normal file
View File

@@ -0,0 +1,116 @@
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
concat: {
js: {
options: {
separator: ';'
},
dist: {
src: ['js/jquery*', 'js/*.js'],
dest: 'dist/<%= pkg.name %>.js'
}
},
sql: {
options: {
banner: '-- >> This file is autogenerated from tables.sql and constraints.sql. Do not modify directly << --'
},
files: {
'database/database.sql': ['database/tables.sql', 'database/constraints.sql'],
'database/migrate_phppos_dist.sql': ['database/tables.sql', 'database/phppos_migrate.sql', 'database/constraints.sql']
}
}
},
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
},
dist: {
files: {
'dist/<%= pkg.name %>.min.js': ['<%= concat.js.dist.dest %>']
}
}
},
jshint: {
files: ['Gruntfile.js', 'js/*.js'],
options: {
// options here to override JSHint defaults
globals: {
jQuery: true,
console: true,
module: true,
document: true
}
}
},
tags: {
js : {
options: {
scriptTemplate: '<script type="text/javascript" src="{{ path }}" language="javascript"></script>',
openTag: '<!-- start js template tags -->',
closeTag: '<!-- end js template tags -->',
absolutePath: true
},
src: [
'js/jquery*.js', 'js/*.js',
],
dest: 'application/views/partial/header.php'
},
minjs : {
options: {
scriptTemplate: '<script type="text/javascript" src="{{ path }}" language="javascript"></script>',
openTag: '<!-- start minjs template tags -->',
closeTag: '<!-- end minjs template tags -->',
absolutePath: true
},
src: [
'dist/*min.js',
],
dest: 'application/views/partial/header.php'
}
},
mochaWebdriver: {
options: {
timeout: 1000 * 60 * 3
},
test : {
options: {
usePhantom: true,
usePromises: true
},
src: ['test/**/*.js']
}
},
watch: {
files: ['<%= jshint.files %>'],
tasks: ['jshint']
},
cachebreaker: {
dev: {
options: {
match: ['opensourcepos.min.js'],
src: {
path: 'dist/opensourcepos.min.js'
},
replacement: 'md5'
},
files: {
src: ['application/views/partial/header.php']
}
}
}
});
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-script-link-tags');
grunt.loadNpmTasks('grunt-mocha-webdriver');
grunt.loadNpmTasks('grunt-cache-breaker');
grunt.registerTask('default', ['tags:js', 'concat', 'uglify', 'tags:minjs', 'cachebreaker']);
};

View File

@@ -1,4 +1,52 @@
ospos
=====
Introduction
------------
Open Source Point of Sale is a web based point of sale system written in the PHP language. It uses MySQL as the data storage back-end and has a simple user interface.
Badges
------
[![Build Status](https://travis-ci.org/jekkos/opensourcepos.svg?branch=master)](https://travis-ci.org/jekkos/opensourcepos)
How to Install
--------------
1. Create/locate a new mysql database to install open source point of sale into
2. Execute the file database/database.sql to create the tables needed
3. unzip and upload Open Source Point of Sale files to web server
4. Copy application/config/database.php.tmpl to application/config/database.php
5. Modify application/config/database.php to connect to your database
6. Modify application/config/config.php encryption key with your own
7. Go to your point of sale install via the browser
8. LOGIN using
username: admin
password:pointofsale
9. Enjoy
FAQ
---
If a blank page (HTTP status 500) shows after search completion or receipt generation, then double check php5-gd presence in your php installation. On windows check in php.ini whether the lib is installed. On Ubuntu issue `sudo apt-get install php5-gd`. Also have a look at the Dockerfile for a complete list of recommended packages.
13/01/2016: Install using Docker
-------------------------------
From now on ospos can be deployed using Docker on Linux, Mac or Windows. This setup dramatically reduces the number of possible issues as all setup is now done in a Dockerfile. Docker runs natively on mac and linux, but will require more overhead on windows. Please refer to the docker documentation for instructions on how to set it up on your platform.
To build and run the image, issue following commands in a terminal with docker installed
docker build -t me/ospos https://github.com/jekkos/opensourcepos.git
docker run -d -p 80:80 me/ospos
Docker will clone the latest master into the image and start a LAMP stack with the application configured. If you like to persist your changes in this install, then you can use two docker data containers to store database and filesystem changes. In this case you will need following command (first time only)
docker run -d -v /app --name="ospos" -v /var/lib/mysql --name="ospos-sql" -p 127.0.0.1:80:80 me/ospos
After stopping the created container for the first time, this command will be replaced with
docker run -d -v /app --volumes-from="ospos" -v /var/lib/mysql --volumes-from="ospos-sql" -p 127.0.0.1:80:80 me/ospos
Both the data and mysql directories will be persisted in a separate docker container and can be mounted within any other container using the last command. A more extensive setup guide can be found at [this site](http://www.opensourceposguide.com/guide/gettingstarted/installation)
If you like the project, and you are making money out of it on a daily basis, then consider to buy me a coffee so I can keep adding features.
[![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=MUN6AEG7NY6H8)
Open Source Point of Sale

View File

@@ -1,13 +0,0 @@
How to Install
-------------------------
1. Create/locate a new mysql database to install open source point of sale into
2. Execute the file database/database.sql to create the tables needed
3. unzip and upload Open Source Point of Sale files to web server
4. Copy application/config/database.php.tmpl to application/config/database.php
5. Modify application/config/database.php to connect to your database
6. Modify application/config/config.php encryption key with your own
7. Go to your point of sale install via the browser
8. LOGIN using
username: admin
password:pointofsale
9. Enjoy

View File

@@ -1,4 +1,14 @@
How to Upgrade
-------------------------
1. Replace all code from your point of sale installation with the code downloaded
2. Run the necessary database upgrades from the database folder
1. Backup all your current database and OSPOS code
2. Make sure you have a copy of application/config/config.php and application/config/database.php
3. Remove all directories
4. Install tne new OSPOS
5. Run the database upgrade scripts from database/ (check which ones you need according to the version you are upgrading from)
6. Take the saved old config.php and upgrade the new config.php with any additional changes you made in the old.
Take time to understand if new config rules require some changes (e.g. encryption keys)
7. Copy application/config/database.php.tmpl to application/config/database.php
8. Take the saved old database.php and change the new database.php to contain all the configuration you had in the old setup.
Please try not to use the old layout, use the new one and just copy the content of the config variables
9. Once new code is in place, database is updated and config files are sorted you are good to start the new OSPOS
10. If any issue please check GitHub issues as somebody else might have had your problem already or post a question

View File

@@ -1,12 +1,120 @@
* Ver. 2.1.0
Version 2.4
-----------
+ *CodeIgniter 3.0.5* Upgrade (please read UPGRADE.txt)
+ Fix for spurious logouts
+ Apache .htaccess mod_expiry caching and security optimizations
+ Bulk item edit fixes (category, tax and supplier fields)
+ Remove f-key shortcuts used for module navigation
+ Allow to use custom invoice numbers when suspending sale
+ PHP7 fixes
+ Specific warnings to distinguish between reorder level and out of stock situation in sales
+ Fix malware detection issues due to usage of base64 encoding for storing session variables
+ Improve language generation scripts (use PHP builtin functionality)
+ Add extra buttons for navigation and printing to receipt and invoice
+ Improve print layout for invoices
+ Make layout consistent for items between receipt and invoice templates
+ Minor bugfixes
Version 2.3.4
-------------
+ Migration script fixes
+ Improved continuous integration setup
+ More integration tests
+ Virtualized container setup (docker install)
+ Live clock functionality + favicon
+ Improved PHP 7 compatbility
+ Added de_CH (German) as language
+ Minor code cleanup
+ Removal of annoying backup prompt on logout
Version 2.3.3
-------------
+ Item kit fixes (search, list, ..)
+ Add datepicker widgets in sale/receiving edit forms
+ Add date filter in items module
+ Add barcode generation logic for EAN8, EAN13
+ Add barcode validation + fallback logic for EAN8, EAN13
+ New config option to generate barcodes if item_number empty
+ Add cost + count to inventory reports
+ Giftcard fixes
+ Refactor sales overview (added date filtering + search options)
+ Better locale config support
+ Improve php compatibility
+ Fix invoice numbering bug on suspend
+ Add configurable locale-dependent dateformat
+ Add grunt-cache-breaker plugin
+ Suspend button appeaers before adding a payment
+ Searching of deleted items, filtering part is removed
+ Remove infamous "0" after leaving sale or receiving comments empty
+ Add SQL script to clean zeroes in sales/receivings comments
+ Numerous other bug fixes
Version 2.3.2
-------------
+ Nominatim (OpenStreetMap) customer address autocompletion
+ Sale invoice templating
+ Configurable barcode generation for items
+ Stock location filtering in detailed sales and receivings reports
+ Giftcards bugfixes
+ Proper pagination support for most modules
+ Language updates
+ Bugfix for decimal taxrates
+ Add gender + company name attributes to customer
+ Stock location config screen refactor
+ Basic travis-ci + phantomJs setup
+ Database backup on admin logout
+ Modifiable item thumbnails
+ Email invoice PDF generation using DomPDF
+ Modifiable company logo
+ jQuery upgrade (1.2 -> 1.8.3)
+ Javascript minification (using grunt)
+ Numerous bugfixes
Version 2.3.1
-------------
+ Extra report permissions (this includes a refactoring of the database model - new grants table)
+ Tax inclusive/exclusive pricing
+ Receivings amount multiplication (can be configured in items section)
+ Customizable sale and receiving numbering
+ Giftcard improvements
+ Fix item import through csv
+ Bug fixes for reports
Version 2.3
-----------
+ Support for multiple stock locations
Version 2.2.2
-------------
+ French language added
+ Thai language added
+ Upgrade to CodeIgniter 2.2 (contains several security fixes)
+ Database types for amounts all changed to decimal types (this will fix rounding errors in the sales and receivings reports) the rest of the application
+ Fix duplicated session cookies in http headers (this broke the application when running on nginx)
Version 2.1.1
-------------
+ Barcodes on the order receipt weren't generated correctly
+ Sales edit screen for detailed sales reports is now available with thickbox as in the rest of the application
+ Indonesian language files updated (Oktafianus)
+ Default language set to 'en' in config.php
+ Fix some css bugs in suspended sales section
+ Default cookie sess_time_expire set to 86400 (24h)
Version 2.1.0
-------------
+ Various upgrades, too numerous to list here.
+ Removed dependancy on ofc upload library due to vulnerability found.
-------------------------------------------------------------------------------
* Ver. 2.0.2
Version 2.0.2
-------------
+ Fixed multiple giftcards issue per Bug #4 reported on Sourceforge where a
second giftcard added would have its balance set to $0 even if the sale did
not require the total of the second giftcard to pay the remaining amount due.
+ Small code cleanup
-------------------------------------------------------------------------------
Version 2.1.0
-------------
* Upgrade to CodeIgniter 2.1.0
* Various small improvements
* Various small improvements

View File

@@ -1 +1,6 @@
Deny from all
<IfModule authz_core_module>
Require all denied
</IfModule>
<IfModule !authz_core_module>
Deny from all
</IfModule>

View File

@@ -1 +1,6 @@
deny from all
<IfModule authz_core_module>
Require all denied
</IfModule>
<IfModule !authz_core_module>
Deny from all
</IfModule>

View File

@@ -1,3 +1,4 @@
<!DOCTYPE html>
<html>
<head>
<title>403 Forbidden</title>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
</html>
</html>

View File

@@ -1,4 +1,6 @@
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
/*
| -------------------------------------------------------------------
| AUTO-LOADER
@@ -20,40 +22,58 @@
|
| 1. Packages
| 2. Libraries
| 3. Helper files
| 4. Custom config files
| 5. Language files
| 6. Models
| 3. Drivers
| 4. Helper files
| 5. Custom config files
| 6. Language files
| 7. Models
|
*/
/*
| -------------------------------------------------------------------
| Auto-load Packges
| Auto-load Packages
| -------------------------------------------------------------------
| Prototype:
|
| $autoload['packages'] = array(APPPATH.'third_party', '/usr/local/shared');
|
*/
$autoload['packages'] = array();
/*
| -------------------------------------------------------------------
| Auto-load Libraries
| -------------------------------------------------------------------
| These are the classes located in the system/libraries folder
| or in your application/libraries folder.
| These are the classes located in system/libraries/ or your
| application/libraries/ directory, with the addition of the
| 'database' library, which is somewhat of a special case.
|
| Prototype:
|
| $autoload['libraries'] = array('database', 'session', 'xmlrpc');
| $autoload['libraries'] = array('database', 'email', 'session');
|
| You can also supply an alternative library name to be assigned
| in the controller:
|
| $autoload['libraries'] = array('user_agent' => 'ua');
*/
$autoload['libraries'] = array('database', 'form_validation', 'session', 'user_agent', 'pagination');
$autoload['libraries'] = array('database','form_validation','session','user_agent', 'pagination');
/*
| -------------------------------------------------------------------
| Auto-load Drivers
| -------------------------------------------------------------------
| These classes are located in system/libraries/ or in your
| application/libraries/ directory, but are also placed inside their
| own subdirectory and they extend the CI_Driver_Library class. They
| offer multiple interchangeable driver options.
|
| Prototype:
|
| $autoload['drivers'] = array('cache');
*/
$autoload['drivers'] = array();
/*
| -------------------------------------------------------------------
@@ -63,9 +83,7 @@ $autoload['libraries'] = array('database','form_validation','session','user_agen
|
| $autoload['helper'] = array('url', 'file');
*/
$autoload['helper'] = array('form','url','table','text','currency', 'html', 'download', 'directory');
$autoload['helper'] = array('form', 'url', 'table', 'text', 'currency', 'html', 'download', 'directory', 'dateformat_helper');
/*
| -------------------------------------------------------------------
@@ -79,10 +97,8 @@ $autoload['helper'] = array('form','url','table','text','currency', 'html', 'dow
| config files. Otherwise, leave it blank.
|
*/
$autoload['config'] = array();
/*
| -------------------------------------------------------------------
| Auto-load Language files
@@ -95,22 +111,19 @@ $autoload['config'] = array();
| "codeigniter_lang.php" would be referenced as array('codeigniter');
|
*/
$autoload['language'] = array();
/*
| -------------------------------------------------------------------
| Auto-load Models
| -------------------------------------------------------------------
| Prototype:
|
| $autoload['model'] = array('model1', 'model2');
| $autoload['model'] = array('first_model', 'second_model');
|
| You can also supply an alternative model name to be assigned
| in the controller:
|
| $autoload['model'] = array('first_model' => 'first');
*/
$autoload['model'] = array('Appconfig','Person','Customer','Employee','Module','Item', 'Item_taxes', 'Sale', 'Sale_suspended', 'Supplier','Inventory','Receiving','Giftcard', 'Item_kit', 'Item_kit_items','Stock_locations','Item_quantities');
/* End of file autoload.php */
/* Location: ./application/config/autoload.php */
$autoload['model'] = array('Appconfig', 'Person', 'Customer', 'Employee', 'Module', 'Item', 'Item_taxes', 'Sale', 'Sale_suspended', 'Supplier', 'Inventory', 'Receiving', 'Giftcard', 'Item_kit', 'Item_kit_items', 'Stock_location', 'Item_quantity');

View File

@@ -1,4 +1,5 @@
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
/*
|--------------------------------------------------------------------------
@@ -9,7 +10,7 @@
|
|
*/
$config['application_version'] = '2.3';
$config['application_version'] = '2.4.0';
/*
|--------------------------------------------------------------------------
@@ -21,8 +22,16 @@ $config['application_version'] = '2.3';
|
| http://example.com/
|
| If this is not set then CodeIgniter will guess the protocol, domain and
| path to your installation.
| WARNING: You MUST set this value!
|
| If it is not set, then CodeIgniter will try guess the protocol and path
| your installation, but due to security concerns the hostname will be set
| to $_SERVER['SERVER_ADDR'] if available, or localhost otherwise.
| The auto-detection mechanism exists only for convenience during
| development and MUST NOT be used in production!
|
| If you need to allow multiple domains, remember that this file is still
| a PHP script and you can easily do that on your own.
|
*/
$config['base_url'] = ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == "on") ? "https" : "http");
@@ -47,17 +56,16 @@ $config['index_page'] = 'index.php';
|--------------------------------------------------------------------------
|
| This item determines which server global should be used to retrieve the
| URI string. The default setting of 'AUTO' works for most servers.
| URI string. The default setting of 'REQUEST_URI' works for most servers.
| If your links do not seem to work, try one of the other delicious flavors:
|
| 'AUTO' Default - auto detects
| 'PATH_INFO' Uses the PATH_INFO
| 'QUERY_STRING' Uses the QUERY_STRING
| 'REQUEST_URI' Uses the REQUEST_URI
| 'ORIG_PATH_INFO' Uses the ORIG_PATH_INFO
| 'REQUEST_URI' Uses $_SERVER['REQUEST_URI']
| 'QUERY_STRING' Uses $_SERVER['QUERY_STRING']
| 'PATH_INFO' Uses $_SERVER['PATH_INFO']
|
| WARNING: If you set this to 'PATH_INFO', URIs will always be URL-decoded!
*/
$config['uri_protocol'] = 'AUTO';
$config['uri_protocol'] = 'REQUEST_URI';
/*
|--------------------------------------------------------------------------
@@ -67,9 +75,8 @@ $config['uri_protocol'] = 'AUTO';
| This option allows you to add a suffix to all URLs generated by CodeIgniter.
| For more information please see the user guide:
|
| http://codeigniter.com/user_guide/general/urls.html
| https://codeigniter.com/user_guide/general/urls.html
*/
$config['url_suffix'] = '';
/*
@@ -92,6 +99,8 @@ $config['language'] = 'en';
| This determines which character set is used by default in various methods
| that require a character set to be provided.
|
| See http://php.net/htmlspecialchars for a list of supported charsets.
|
*/
$config['charset'] = 'UTF-8';
@@ -106,7 +115,6 @@ $config['charset'] = 'UTF-8';
*/
$config['enable_hooks'] = TRUE;
/*
|--------------------------------------------------------------------------
| Class Extension Prefix
@@ -115,33 +123,56 @@ $config['enable_hooks'] = TRUE;
| This item allows you to set the filename/classname prefix when extending
| native libraries. For more information please see the user guide:
|
| http://codeigniter.com/user_guide/general/core_classes.html
| http://codeigniter.com/user_guide/general/creating_libraries.html
| https://codeigniter.com/user_guide/general/core_classes.html
| https://codeigniter.com/user_guide/general/creating_libraries.html
|
*/
$config['subclass_prefix'] = 'MY_';
/*
|--------------------------------------------------------------------------
| Composer auto-loading
|--------------------------------------------------------------------------
|
| Enabling this setting will tell CodeIgniter to look for a Composer
| package auto-loader script in application/vendor/autoload.php.
|
| $config['composer_autoload'] = TRUE;
|
| Or if you have your vendor/ directory located somewhere else, you
| can opt to set a specific path as well:
|
| $config['composer_autoload'] = '/path/to/vendor/autoload.php';
|
| For more information about Composer, please visit http://getcomposer.org/
|
| Note: This will NOT disable or override the CodeIgniter-specific
| autoloading (application/config/autoload.php)
*/
$config['composer_autoload'] = FALSE;
/*
|--------------------------------------------------------------------------
| Allowed URL Characters
|--------------------------------------------------------------------------
|
| This lets you specify with a regular expression which characters are permitted
| within your URLs. When someone tries to submit a URL with disallowed
| characters they will get a warning message.
| This lets you specify which characters are permitted within your URLs.
| When someone tries to submit a URL with disallowed characters they will
| get a warning message.
|
| As a security measure you are STRONGLY encouraged to restrict URLs to
| as few characters as possible. By default only these are allowed: a-z 0-9~%.:_-
|
| Leave blank to allow all characters -- but only if you are insane.
|
| The configured value is actually a regular expression character group
| and it will be executed as: ! preg_match('/^[<permitted_uri_chars>]+$/i
|
| DO NOT CHANGE THIS UNLESS YOU FULLY UNDERSTAND THE REPERCUSSIONS!!
|
*/
$config['permitted_uri_chars'] = 'a-z 0-9~%.:_\-';
/*
|--------------------------------------------------------------------------
| Enable Query Strings
@@ -167,19 +198,17 @@ $config['permitted_uri_chars'] = 'a-z 0-9~%.:_\-';
| use segment based URLs.
|
*/
$config['allow_get_array'] = TRUE;
$config['allow_get_array'] = TRUE;
$config['enable_query_strings'] = FALSE;
$config['controller_trigger'] = 'c';
$config['function_trigger'] = 'm';
$config['directory_trigger'] = 'd'; // experimental not currently in use
$config['controller_trigger'] = 'c';
$config['function_trigger'] = 'm';
$config['directory_trigger'] = 'd';
/*
|--------------------------------------------------------------------------
| Error Logging Threshold
|--------------------------------------------------------------------------
|
| If you have enabled error logging, you can set an error threshold to
| determine what gets logged. Threshold options are:
| You can enable error logging by setting a threshold over zero. The
| threshold determines what gets logged. Threshold options are:
|
@@ -189,6 +218,10 @@ $config['directory_trigger'] = 'd'; // experimental not currently in use
| 3 = Informational Messages
| 4 = All Messages
|
| You can also pass an array with threshold levels to show individual error types
|
| array(2) = Debug Messages, without Error Messages
|
| For a live site you'll usually only enable Errors (1) to be logged otherwise
| your log files will fill up very fast.
|
@@ -201,11 +234,37 @@ $config['log_threshold'] = 0;
|--------------------------------------------------------------------------
|
| Leave this BLANK unless you would like to set something other than the default
| application/logs/ folder. Use a full server path with trailing slash.
| application/logs/ directory. Use a full server path with trailing slash.
|
*/
$config['log_path'] = '';
/*
|--------------------------------------------------------------------------
| Log File Extension
|--------------------------------------------------------------------------
|
| The default filename extension for log files. The default 'php' allows for
| protecting the log files via basic scripting, when they are to be stored
| under a publicly accessible directory.
|
| Note: Leaving it blank will default to 'php'.
|
*/
$config['log_file_extension'] = '';
/*
|--------------------------------------------------------------------------
| Log File Permissions
|--------------------------------------------------------------------------
|
| The file system permissions to be applied on newly created log files.
|
| IMPORTANT: This MUST be an integer (no quotes) and you MUST use octal
| integer notation (i.e. 0700, 0644, etc.)
*/
$config['log_file_permissions'] = 0644;
/*
|--------------------------------------------------------------------------
| Date Format for Logs
@@ -217,71 +276,152 @@ $config['log_path'] = '';
*/
$config['log_date_format'] = 'Y-m-d H:i:s';
/*
|--------------------------------------------------------------------------
| Error Views Directory Path
|--------------------------------------------------------------------------
|
| Leave this BLANK unless you would like to set something other than the default
| application/views/errors/ directory. Use a full server path with trailing slash.
|
*/
$config['error_views_path'] = '';
/*
|--------------------------------------------------------------------------
| Cache Directory Path
|--------------------------------------------------------------------------
|
| Leave this BLANK unless you would like to set something other than the default
| system/cache/ folder. Use a full server path with trailing slash.
| application/cache/ directory. Use a full server path with trailing slash.
|
*/
$config['cache_path'] = '';
/*
|--------------------------------------------------------------------------
| Cache Include Query String
|--------------------------------------------------------------------------
|
| Whether to take the URL query string into consideration when generating
| output cache files. Valid options are:
|
| FALSE = Disabled
| TRUE = Enabled, take all query parameters into account.
| Please be aware that this may result in numerous cache
| files generated for the same page over and over again.
| array('q') = Enabled, but only take into account the specified list
| of query parameters.
|
*/
$config['cache_query_string'] = FALSE;
/*
|--------------------------------------------------------------------------
| Encryption Key
|--------------------------------------------------------------------------
|
| If you use the Encryption class or the Session class you
| MUST set an encryption key. See the user guide for info.
| If you use the Encryption class, you must set an encryption key.
| See the user guide for more info.
|
| https://codeigniter.com/user_guide/libraries/encryption.html
|
*/
$config['encryption_key'] = 'PUT_YOUR_ENCRYPTION_KEY_HERE';
$config['encryption_key'] = '';
/*
|--------------------------------------------------------------------------
| Session Variables
|--------------------------------------------------------------------------
|
| 'sess_cookie_name' = the name you want for the cookie
| 'sess_expiration' = the number of SECONDS you want the session to last.
| by default sessions last 7200 seconds (two hours). Set to zero for no expiration.
| 'sess_expire_on_close' = Whether to cause the session to expire automatically
| when the browser window is closed
| 'sess_encrypt_cookie' = Whether to encrypt the cookie
| 'sess_use_database' = Whether to save the session data to a database
| 'sess_table_name' = The name of the session database table
| 'sess_match_ip' = Whether to match the user's IP address when reading the session data
| 'sess_match_useragent' = Whether to match the User Agent when reading the session data
| 'sess_time_to_update' = how many seconds between CI refreshing Session Information
| 'sess_driver'
|
| The storage driver to use: files, database, redis, memcached
|
| 'sess_cookie_name'
|
| The session cookie name, must contain only [0-9a-z_-] characters
|
| 'sess_expiration'
|
| The number of SECONDS you want the session to last.
| Setting to 0 (zero) means expire when the browser is closed.
|
| 'sess_save_path'
|
| The location to save sessions to, driver dependent.
|
| For the 'files' driver, it's a path to a writable directory.
| WARNING: Only absolute paths are supported!
|
| For the 'database' driver, it's a table name.
| Please read up the manual for the format with other session drivers.
|
| IMPORTANT: You are REQUIRED to set a valid save path!
|
| 'sess_match_ip'
|
| Whether to match the user's IP address when reading the session data.
|
| WARNING: If you're using the database driver, don't forget to update
| your session table's PRIMARY KEY when changing this setting.
|
| 'sess_time_to_update'
|
| How many seconds between CI regenerating the session ID.
|
| 'sess_regenerate_destroy'
|
| Whether to destroy session data associated with the old session ID
| when auto-regenerating the session ID. When set to FALSE, the data
| will be later deleted by the garbage collector.
|
| Other session cookie settings are shared with the rest of the application,
| except for 'cookie_prefix' and 'cookie_httponly', which are ignored here.
|
*/
$config['sess_cookie_name'] = 'ci_session';
$config['sess_expiration'] = 0;
$config['sess_expire_on_close'] = FALSE;
$config['sess_encrypt_cookie'] = FALSE;
$config['sess_use_database'] = TRUE;
$config['sess_table_name'] = 'ospos_sessions';
$config['sess_match_ip'] = FALSE;
$config['sess_match_useragent'] = FAlSE;
$config['sess_time_to_update'] = 86400;
$config['sess_driver'] = 'database';
$config['sess_cookie_name'] = 'ospos_session';
$config['sess_expiration'] = 7200;
$config['sess_save_path'] = 'sessions';
$config['sess_match_ip'] = FALSE;
$config['sess_time_to_update'] = 300;
$config['sess_regenerate_destroy'] = FALSE;
/*
|--------------------------------------------------------------------------
| Cookie Related Variables
|--------------------------------------------------------------------------
|
| 'cookie_prefix' = Set a prefix if you need to avoid collisions
| 'cookie_domain' = Set to .your-domain.com for site-wide cookies
| 'cookie_path' = Typically will be a forward slash
| 'cookie_secure' = Cookies will only be set if a secure HTTPS connection exists.
| 'cookie_prefix' = Set a cookie name prefix if you need to avoid collisions
| 'cookie_domain' = Set to .your-domain.com for site-wide cookies
| 'cookie_path' = Typically will be a forward slash
| 'cookie_secure' = Cookie will only be set if a secure HTTPS connection exists.
| 'cookie_httponly' = Cookie will only be accessible via HTTP(S) (no javascript)
|
| Note: These settings (with the exception of 'cookie_prefix' and
| 'cookie_httponly') will also affect sessions.
|
*/
$config['cookie_prefix'] = "";
$config['cookie_domain'] = "";
$config['cookie_path'] = "/";
$config['cookie_prefix'] = '';
$config['cookie_domain'] = '';
$config['cookie_path'] = '/';
$config['cookie_secure'] = FALSE;
$config['cookie_httponly'] = FALSE;
/*
|--------------------------------------------------------------------------
| Standardize newlines
|--------------------------------------------------------------------------
|
| Determines whether to standardize newline characters in input data,
| meaning to replace \r\n, \r, \n occurrences with the PHP_EOL value.
|
| This is particularly useful for portability between UNIX-based OSes,
| (usually \n) and Windows (\r\n).
|
*/
$config['standardize_newlines'] = FALSE;
/*
|--------------------------------------------------------------------------
@@ -291,6 +431,9 @@ $config['cookie_secure'] = FALSE;
| Determines whether the XSS filter is always active when GET, POST or
| COOKIE data is encountered
|
| WARNING: This feature is DEPRECATED and currently available only
| for backwards compatibility purposes!
|
*/
$config['global_xss_filtering'] = FALSE;
@@ -305,11 +448,15 @@ $config['global_xss_filtering'] = FALSE;
| 'csrf_token_name' = The token name
| 'csrf_cookie_name' = The cookie name
| 'csrf_expire' = The number in seconds the token should expire.
| 'csrf_regenerate' = Regenerate token on every submission
| 'csrf_exclude_uris' = Array of URIs which ignore CSRF checks
*/
$config['csrf_protection'] = FALSE;
$config['csrf_token_name'] = 'csrf_test_name';
$config['csrf_cookie_name'] = 'csrf_cookie_name';
$config['csrf_token_name'] = 'csrf_ospos_v24';
$config['csrf_cookie_name'] = 'csrf_cookie_ospos_v24';
$config['csrf_expire'] = 7200;
$config['csrf_regenerate'] = TRUE;
$config['csrf_exclude_uris'] = array();
/*
|--------------------------------------------------------------------------
@@ -321,6 +468,9 @@ $config['csrf_expire'] = 7200;
| Even if it does, however, not all browsers support compression
| so enable only if you are reasonably sure your visitors can handle it.
|
| Only used if zlib.output_compression is turned off in your php.ini.
| Please do not use it together with httpd-level output compression.
|
| VERY IMPORTANT: If you are getting a blank page when compression is enabled it
| means you are prematurely outputting something to your browser. It could
| even be a line of whitespace at the end of one of your scripts. For
@@ -335,15 +485,14 @@ $config['compress_output'] = FALSE;
| Master Time Reference
|--------------------------------------------------------------------------
|
| Options are 'local' or 'gmt'. This pref tells the system whether to use
| your server's local time as the master 'now' reference, or convert it to
| GMT. See the 'date helper' page of the user guide for information
| regarding date handling.
| Options are 'local' or any PHP supported timezone. This preference tells
| the system whether to use your server's local time as the master 'now'
| reference, or convert it to the configured one timezone. See the 'date
| helper' page of the user guide for information regarding date handling.
|
*/
$config['time_reference'] = 'local';
/*
|--------------------------------------------------------------------------
| Rewrite PHP Short Tags
@@ -353,23 +502,25 @@ $config['time_reference'] = 'local';
| can rewrite the tags on-the-fly, enabling you to utilize that syntax
| in your view files. Options are TRUE or FALSE (boolean)
|
| Note: You need to have eval() enabled for this to work.
|
*/
$config['rewrite_short_tags'] = FALSE;
/*
|--------------------------------------------------------------------------
| Reverse Proxy IPs
|--------------------------------------------------------------------------
|
| If your server is behind a reverse proxy, you must whitelist the proxy IP
| addresses from which CodeIgniter should trust the HTTP_X_FORWARDED_FOR
| header in order to properly identify the visitor's IP address.
| Comma-delimited, e.g. '10.0.1.200,10.0.1.201'
| If your server is behind a reverse proxy, you must whitelist the proxy
| IP addresses from which CodeIgniter should trust headers such as
| HTTP_X_FORWARDED_FOR and HTTP_CLIENT_IP in order to properly identify
| the visitor's IP address.
|
| You can use both an array or a comma-separated list of proxy addresses,
| as well as specifying whole subnets. Here are a few examples:
|
| Comma-separated: '10.0.1.200,192.168.5.0/24'
| Array: array('10.0.1.200', '192.168.5.0/24')
*/
$config['proxy_ips'] = '';
/* End of file config.php */
/* Location: ./application/config/config.php */

View File

@@ -1,4 +1,17 @@
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
/*
|--------------------------------------------------------------------------
| Display Debug backtrace
|--------------------------------------------------------------------------
|
| If set to TRUE, a backtrace will be displayed along with php errors. If
| error_reporting is disabled, the backtrace will not display, regardless
| of this setting
|
*/
defined('SHOW_DEBUG_BACKTRACE') OR define('SHOW_DEBUG_BACKTRACE', TRUE);
/*
|--------------------------------------------------------------------------
@@ -13,10 +26,10 @@
| always be used to set the mode correctly.
|
*/
define('FILE_READ_MODE', 0644);
define('FILE_WRITE_MODE', 0666);
define('DIR_READ_MODE', 0755);
define('DIR_WRITE_MODE', 0777);
defined('FILE_READ_MODE') OR define('FILE_READ_MODE', 0644);
defined('FILE_WRITE_MODE') OR define('FILE_WRITE_MODE', 0666);
defined('DIR_READ_MODE') OR define('DIR_READ_MODE', 0755);
defined('DIR_WRITE_MODE') OR define('DIR_WRITE_MODE', 0755);
/*
|--------------------------------------------------------------------------
@@ -26,16 +39,52 @@ define('DIR_WRITE_MODE', 0777);
| These modes are used when working with fopen()/popen()
|
*/
defined('FOPEN_READ') OR define('FOPEN_READ', 'rb');
defined('FOPEN_READ_WRITE') OR define('FOPEN_READ_WRITE', 'r+b');
defined('FOPEN_WRITE_CREATE_DESTRUCTIVE') OR define('FOPEN_WRITE_CREATE_DESTRUCTIVE', 'wb'); // truncates existing file data, use with care
defined('FOPEN_READ_WRITE_CREATE_DESCTRUCTIVE') OR define('FOPEN_READ_WRITE_CREATE_DESTRUCTIVE', 'w+b'); // truncates existing file data, use with care
defined('FOPEN_WRITE_CREATE') OR define('FOPEN_WRITE_CREATE', 'ab');
defined('FOPEN_READ_WRITE_CREATE') OR define('FOPEN_READ_WRITE_CREATE', 'a+b');
defined('FOPEN_WRITE_CREATE_STRICT') OR define('FOPEN_WRITE_CREATE_STRICT', 'xb');
defined('FOPEN_READ_WRITE_CREATE_STRICT') OR define('FOPEN_READ_WRITE_CREATE_STRICT', 'x+b');
define('FOPEN_READ', 'rb');
define('FOPEN_READ_WRITE', 'r+b');
define('FOPEN_WRITE_CREATE_DESTRUCTIVE', 'wb'); // truncates existing file data, use with care
define('FOPEN_READ_WRITE_CREATE_DESTRUCTIVE', 'w+b'); // truncates existing file data, use with care
define('FOPEN_WRITE_CREATE', 'ab');
define('FOPEN_READ_WRITE_CREATE', 'a+b');
define('FOPEN_WRITE_CREATE_STRICT', 'xb');
define('FOPEN_READ_WRITE_CREATE_STRICT', 'x+b');
/*
|--------------------------------------------------------------------------
| Exit Status Codes
|--------------------------------------------------------------------------
|
| Used to indicate the conditions under which the script is exit()ing.
| While there is no universal standard for error codes, there are some
| broad conventions. Three such conventions are mentioned below, for
| those who wish to make use of them. The CodeIgniter defaults were
| chosen for the least overlap with these conventions, while still
| leaving room for others to be defined in future versions and user
| applications.
|
| The three main conventions used for determining exit status codes
| are as follows:
|
| Standard C/C++ Library (stdlibc):
| http://www.gnu.org/software/libc/manual/html_node/Exit-Status.html
| (This link also contains other GNU-specific conventions)
| BSD sysexits.h:
| http://www.gsp.com/cgi-bin/man.cgi?section=3&topic=sysexits
| Bash scripting:
| http://tldp.org/LDP/abs/html/exitcodes.html
|
*/
defined('EXIT_SUCCESS') OR define('EXIT_SUCCESS', 0); // no errors
defined('EXIT_ERROR') OR define('EXIT_ERROR', 1); // generic error
defined('EXIT_CONFIG') OR define('EXIT_CONFIG', 3); // configuration error
defined('EXIT_UNKNOWN_FILE') OR define('EXIT_UNKNOWN_FILE', 4); // file not found
defined('EXIT_UNKNOWN_CLASS') OR define('EXIT_UNKNOWN_CLASS', 5); // unknown class
defined('EXIT_UNKNOWN_METHOD') OR define('EXIT_UNKNOWN_METHOD', 6); // unknown class member
defined('EXIT_USER_INPUT') OR define('EXIT_USER_INPUT', 7); // invalid user input
defined('EXIT_DATABASE') OR define('EXIT_DATABASE', 8); // database error
defined('EXIT__AUTO_MIN') OR define('EXIT__AUTO_MIN', 9); // lowest automatically-assigned error code
defined('EXIT__AUTO_MAX') OR define('EXIT__AUTO_MAX', 125); // highest automatically-assigned error code
/* End of file constants.php */
/* Location: ./application/config/constants.php */
/*
| Precision for calculations performed on decimals
*/
define("PRECISION", 5);

View File

@@ -1,4 +1,6 @@
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
/*
| -------------------------------------------------------------------
| DATABASE CONNECTIVITY SETTINGS
@@ -12,14 +14,17 @@
| EXPLANATION OF VARIABLES
| -------------------------------------------------------------------
|
| ['dsn'] The full DSN string describe a connection to the database.
| ['hostname'] The hostname of your database server.
| ['username'] The username used to connect to the database
| ['password'] The password used to connect to the database
| ['database'] The name of the database you want to connect to
| ['dbdriver'] The database type. ie: mysql. Currently supported:
mysql, mysqli, postgre, odbc, mssql, sqlite, oci8
| ['dbdriver'] The database driver. e.g.: mysqli.
| Currently supported:
| cubrid, ibase, mssql, mysql, mysqli, oci8,
| odbc, pdo, postgre, sqlite, sqlite3, sqlsrv
| ['dbprefix'] You can add an optional prefix, which will be added
| to the table name when using the Active Record class
| to the table name when using the Query Builder class
| ['pconnect'] TRUE/FALSE - Whether to use a persistent connection
| ['db_debug'] TRUE/FALSE - Whether database errors should be displayed.
| ['cache_on'] TRUE/FALSE - Enables/disables query caching
@@ -34,36 +39,58 @@
| multi-byte character set and are running versions lower than these.
| Sites using Latin-1 or UTF-8 database character set and collation are unaffected.
| ['swap_pre'] A default table prefix that should be swapped with the dbprefix
| ['autoinit'] Whether or not to automatically initialize the database.
| ['encrypt'] Whether or not to use an encrypted connection.
|
| 'mysql' (deprecated), 'sqlsrv' and 'pdo/sqlsrv' drivers accept TRUE/FALSE
| 'mysqli' and 'pdo/mysql' drivers accept an array with the following options:
|
| 'ssl_key' - Path to the private key file
| 'ssl_cert' - Path to the public key certificate file
| 'ssl_ca' - Path to the certificate authority file
| 'ssl_capath' - Path to a directory containing trusted CA certificats in PEM format
| 'ssl_cipher' - List of *allowed* ciphers to be used for the encryption, separated by colons (':')
| 'ssl_verify' - TRUE/FALSE; Whether verify the server certificate or not ('mysqli' only)
|
| ['compress'] Whether or not to use client compression (MySQL only)
| ['stricton'] TRUE/FALSE - forces 'Strict Mode' connections
| - good for ensuring strict SQL while developing
| ['ssl_options'] Used to set various SSL options that can be used when making SSL connections.
| ['failover'] array - A array with 0 or more data for connections if the main should fail.
| ['save_queries'] TRUE/FALSE - Whether to "save" all executed queries.
| NOTE: Disabling this will also effectively disable both
| $this->db->last_query() and profiling of DB queries.
| When you run a query, with this setting set to TRUE (default),
| CodeIgniter will store the SQL statement for debugging purposes.
| However, this may cause high memory usage, especially if you run
| a lot of SQL queries ... disable this to avoid that problem.
|
| The $active_group variable lets you choose which connection group to
| make active. By default there is only one group (the 'default' group).
|
| The $active_record variables lets you determine whether or not to load
| the active record class
| The $query_builder variables lets you determine whether or not to load
| the query builder class.
*/
$active_group = 'default';
$active_record = TRUE;
$query_builder = TRUE;
$db['default']['hostname'] = 'localhost';
$db['default']['username'] = '';
$db['default']['password'] = '';
$db['default']['database'] = '';
$db['default']['dbdriver'] = 'mysql';
$db['default']['dbprefix'] = 'ospos_';
$db['default']['pconnect'] = FALSE;
$db['default']['db_debug'] = TRUE;
$db['default']['cache_on'] = FALSE;
$db['default']['cachedir'] = '';
$db['default']['char_set'] = 'utf8';
$db['default']['dbcollat'] = 'utf8_general_ci';
$db['default']['swap_pre'] = '';
$db['default']['autoinit'] = TRUE;
$db['default']['stricton'] = FALSE;
/* End of file database.php */
/* Location: ./application/config/database.php */
$db['default'] = array(
'dsn' => '',
'hostname' => 'localhost',
'username' => '',
'password' => '',
'database' => '',
'dbdriver' => 'mysqli',
'dbprefix' => 'ospos_',
'pconnect' => FALSE,
'db_debug' => (ENVIRONMENT !== 'production'),
'cache_on' => FALSE,
'cachedir' => '',
'char_set' => 'utf8',
'dbcollat' => 'utf8_general_ci',
'swap_pre' => '',
'encrypt' => FALSE,
'compress' => FALSE,
'stricton' => FALSE,
'failover' => array(),
'save_queries' => TRUE
);

View File

@@ -1,15 +1,24 @@
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
$_doctypes = array(
'xhtml11' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">',
'xhtml1-strict' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">',
'xhtml1-trans' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
'xhtml1-frame' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">',
'html5' => '<!DOCTYPE html>',
'html4-strict' => '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">',
'html4-trans' => '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">',
'html4-frame' => '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">'
);
/* End of file doctypes.php */
/* Location: ./application/config/doctypes.php */
'xhtml11' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">',
'xhtml1-strict' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">',
'xhtml1-trans' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
'xhtml1-frame' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">',
'xhtml-basic11' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">',
'html5' => '<!DOCTYPE html>',
'html4-strict' => '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">',
'html4-trans' => '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">',
'html4-frame' => '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">',
'mathml1' => '<!DOCTYPE math SYSTEM "http://www.w3.org/Math/DTD/mathml1/mathml.dtd">',
'mathml2' => '<!DOCTYPE math PUBLIC "-//W3C//DTD MathML 2.0//EN" "http://www.w3.org/Math/DTD/mathml2/mathml2.dtd">',
'svg10' => '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">',
'svg11' => '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">',
'svg11-basic' => '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Basic//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-basic.dtd">',
'svg11-tiny' => '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd">',
'xhtml-math-svg-xh' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN" "http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd">',
'xhtml-math-svg-sh' => '<!DOCTYPE svg:svg PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN" "http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd">',
'xhtml-rdfa-1' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd">',
'xhtml-rdfa-2' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.1//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-2.dtd">'
);

View File

@@ -1,4 +1,6 @@
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
/*
| -------------------------------------------------------------------
| Foreign Characters
@@ -14,51 +16,88 @@ $foreign_characters = array(
'/Ä/' => 'Ae',
'/Ü/' => 'Ue',
'/Ö/' => 'Oe',
'/À|Á|Â|Ã|Ä|Å|Ǻ|Ā|Ă|Ą|Ǎ/' => 'A',
'/à|á|â|ã|å|ǻ|ā|ă|ą|ǎ|ª/' => 'a',
'/À|Á|Â|Ã|Ä|Å|Ǻ|Ā|Ă|Ą|Ǎ|Α|Ά|Ả|Ạ|Ầ|Ẫ|Ẩ|Ậ|Ằ|Ắ|Ẵ|Ẳ|Ặ|А/' => 'A',
'/à|á|â|ã|å|ǻ|ā|ă|ą|ǎ|ª|α|ά|ả|ạ|ầ|ấ|ẫ|ẩ|ậ|ằ|ắ|ẵ|ẳ|ặ|а/' => 'a',
'/Б/' => 'B',
'/б/' => 'b',
'/Ç|Ć|Ĉ|Ċ|Č/' => 'C',
'/ç|ć|ĉ|ċ|č/' => 'c',
'/Ð|Ď|Đ/' => 'D',
'/ð|ď|đ/' => 'd',
'/È|É|Ê|Ë|Ē|Ĕ|Ė|Ę|Ě/' => 'E',
'/è|é|ê|ë|ē|ĕ|ė|ę|ě/' => 'e',
'/Ĝ|Ğ|Ġ|Ģ/' => 'G',
'/ĝ|ğ|ġ|ģ/' => 'g',
'/Д/' => 'D',
'/д/' => 'd',
'/Ð|Ď|Đ|Δ/' => 'Dj',
'/ð|ď|đ|δ/' => 'dj',
'/È|É|Ê|Ë|Ē|Ĕ|Ė|Ę|Ě|Ε|Έ|Ẽ|Ẻ|Ẹ|Ề|Ế|Ễ|Ể|Ệ|Е/' => 'E',
'/è|é|ê|ë|ē|ĕ|ė|ę|ě|έ|ε|ẽ|ẻ|ẹ|ề|ế|ễ|ể|ệ|е/' => 'e',
'/Ф/' => 'F',
'/ф/' => 'f',
'/Ĝ|Ğ|Ġ|Ģ|Γ|Г|Ґ/' => 'G',
'/ĝ|ğ|ġ|ģ|γ|г|ґ/' => 'g',
'/Ĥ|Ħ/' => 'H',
'/ĥ|ħ/' => 'h',
'/Ì|Í|Î|Ï|Ĩ|Ī|Ĭ|Ǐ|Į|İ/' => 'I',
'/ì|í|î|ï|ĩ|ī|ĭ|ǐ|į|ı/' => 'i',
'/Ì|Í|Î|Ï|Ĩ|Ī|Ĭ|Ǐ|Į|İ|Η|Ή|Ί|Ι|Ϊ|Ỉ|Ị|И|Ы/' => 'I',
'/ì|í|î|ï|ĩ|ī|ĭ|ǐ|į|ı|η|ή|ί|ι|ϊ|ỉ|ị|и|ы|ї/' => 'i',
'/Ĵ/' => 'J',
'/ĵ/' => 'j',
'/Ķ/' => 'K',
'/ķ/' => 'k',
'/Ĺ|Ļ|Ľ|Ŀ|Ł/' => 'L',
'/ĺ|ļ|ľ|ŀ|ł/' => 'l',
'/Ñ|Ń|Ņ|Ň/' => 'N',
'/ñ|ń|ņ|ň|ʼn/' => 'n',
'/Ò|Ó|Ô|Õ|Ō|Ŏ|Ǒ|Ő|Ơ|Ø|Ǿ/' => 'O',
'/ò|ó|ô|õ|ō|ŏ|ǒ|ő|ơ|ø|ǿ|º/' => 'o',
'/Ŕ|Ŗ|Ř/' => 'R',
'/ŕ|ŗ|ř/' => 'r',
'/Ś|Ŝ|Ş|Š/' => 'S',
'/ś|ŝ|ş|š|ſ/' => 's',
'/Ţ|Ť|Ŧ/' => 'T',
'/ţ|ť|ŧ/' => 't',
'/Ù|Ú|Û|Ũ|Ū|Ŭ|Ů|Ű|Ų|Ư|Ǔ|Ǖ|Ǘ|Ǚ|Ǜ/' => 'U',
'/ù|ú|û|ũ|ū|ŭ|ů|ű|ų|ư|ǔ|ǖ|ǘ|ǚ|ǜ/' => 'u',
'/Ý|Ÿ|Ŷ/' => 'Y',
'/ý|ÿ|ŷ/' => 'y',
'/Ķ|Κ|К/' => 'K',
'/ķ|κ|к/' => 'k',
'/Ĺ|Ļ|Ľ|Ŀ|Ł|Λ|Л/' => 'L',
'/ĺ|ļ|ľ|ŀ|ł|λ|л/' => 'l',
'/М/' => 'M',
'/м/' => 'm',
'/Ñ|Ń|Ņ|Ň|Ν|Н/' => 'N',
'/ñ|ń|ņ|ň|ʼn|ν/' => 'n',
'/Ò|Ó|Ô|Õ|Ō|Ŏ|Ǒ|Ő|Ơ|Ø|Ǿ|Ο|Ό|Ω|Ώ|Ỏ|Ọ|Ồ|Ố|Ỗ|Ổ|Ộ|Ờ|Ớ|Ỡ|Ở|Ợ|О/' => 'O',
'/ò|ó|ô|õ|ō|ŏ|ǒ|ő|ơ|ø|ǿ|º|ο|ό|ω|ώ|ỏ|ọ|ồ|ố|ỗ|ổ|ộ|ờ|ớ|ỡ|ở|ợ|о/' => 'o',
'/П/' => 'P',
'/п/' => 'p',
'/Ŕ|Ŗ|Ř|Ρ|Р/' => 'R',
'/ŕ|ŗ|ř|ρ|р/' => 'r',
'/Ś|Ŝ|Ş|Ș|Š|Σ|С/' => 'S',
'/ś|ŝ|ş|ș|š|ſ|σ|ς|с/' => 's',
'/Ț|Ţ|Ť|Ŧ|τ|Т/' => 'T',
'/ț|ţ|ť|ŧ|т/' => 't',
'/Þ|þ/' => 'th',
'/Ù|Ú|Û|Ũ|Ū|Ŭ|Ů|Ű|Ų|Ư|Ǔ|Ǖ|Ǘ|Ǚ|Ǜ|Ũ|Ủ|Ụ|Ừ|Ứ|Ữ|Ử|Ự|У/' => 'U',
'/ù|ú|û|ũ|ū|ŭ|ů|ű|ų|ư|ǔ|ǖ|ǘ|ǚ|ǜ|υ|ύ|ϋ|ủ|ụ|ừ|ứ|ữ|ử|ự|у/' => 'u',
'/Ý|Ÿ|Ŷ|Υ|Ύ|Ϋ|Ỳ|Ỹ|Ỷ|Ỵ|Й/' => 'Y',
'/ý|ÿ|ŷ|ỳ|ỹ|ỷ|ỵ|й/' => 'y',
'/В/' => 'V',
'/в/' => 'v',
'/Ŵ/' => 'W',
'/ŵ/' => 'w',
'/Ź|Ż|Ž/' => 'Z',
'/ź|ż|ž/' => 'z',
'/Ź|Ż|Ž|Ζ|З/' => 'Z',
'/ź|ż|ž|ζ|з/' => 'z',
'/Æ|Ǽ/' => 'AE',
'/ß/'=> 'ss',
'/ß/' => 'ss',
'/IJ/' => 'IJ',
'/ij/' => 'ij',
'/Œ/' => 'OE',
'/ƒ/' => 'f'
'/ƒ/' => 'f',
'/ξ/' => 'ks',
'/π/' => 'p',
'/β/' => 'v',
'/μ/' => 'm',
'/ψ/' => 'ps',
'/Ё/' => 'Yo',
'/ё/' => 'yo',
'/Є/' => 'Ye',
'/є/' => 'ye',
'/Ї/' => 'Yi',
'/Ж/' => 'Zh',
'/ж/' => 'zh',
'/Х/' => 'Kh',
'/х/' => 'kh',
'/Ц/' => 'Ts',
'/ц/' => 'ts',
'/Ч/' => 'Ch',
'/ч/' => 'ch',
'/Ш/' => 'Sh',
'/ш/' => 'sh',
'/Щ/' => 'Shch',
'/щ/' => 'shch',
'/Ъ|ъ|Ь|ь/' => '',
'/Ю/' => 'Yu',
'/ю/' => 'yu',
'/Я/' => 'Ya',
'/я/' => 'ya'
);
/* End of file foreign_chars.php */
/* Location: ./application/config/foreign_chars.php */

View File

@@ -1,4 +1,6 @@
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
/*
| -------------------------------------------------------------------------
| Hooks
@@ -6,7 +8,7 @@
| This file lets you define "hooks" to extend CI without hacking the core
| files. Please see the user guide for info:
|
| http://codeigniter.com/user_guide/general/hooks.html
| https://codeigniter.com/user_guide/general/hooks.html
|
*/
$hook['post_controller_constructor'] = array(
@@ -15,7 +17,3 @@ $hook['post_controller_constructor'] = array(
'filename' => 'load_config.php',
'filepath' => 'hooks'
);
/* End of file hooks.php */
/* Location: ./application/config/hooks.php */

View File

@@ -1,3 +1,4 @@
<!DOCTYPE html>
<html>
<head>
<title>403 Forbidden</title>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
</html>
</html>

View File

@@ -0,0 +1,19 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
/*
| -------------------------------------------------------------------------
| Memcached settings
| -------------------------------------------------------------------------
| Your Memcached servers can be specified below.
|
| See: https://codeigniter.com/user_guide/libraries/caching.html#memcached
|
*/
$config = array(
'default' => array(
'hostname' => '127.0.0.1',
'port' => '11211',
'weight' => '1',
),
);

View File

@@ -1,40 +1,84 @@
<?php defined('BASEPATH') OR exit('No direct script access allowed');
/*
|--------------------------------------------------------------------------
| Enable/Disable Migrations
|--------------------------------------------------------------------------
|
| Migrations are disabled by default but should be enabled
| whenever you intend to do a schema migration.
|
*/
$config['migration_enabled'] = FALSE;
/*
|--------------------------------------------------------------------------
| Migrations version
|--------------------------------------------------------------------------
|
| This is used to set migration version that the file system should be on.
| If you run $this->migration->latest() this is the version that schema will
| be upgraded / downgraded to.
|
*/
$config['migration_version'] = 0;
/*
|--------------------------------------------------------------------------
| Migrations Path
|--------------------------------------------------------------------------
|
| Path to your migrations folder.
| Typically, it will be within your application path.
| Also, writing permission is required within the migrations path.
|
*/
$config['migration_path'] = APPPATH . 'migrations/';
/* End of file migration.php */
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
/*
|--------------------------------------------------------------------------
| Enable/Disable Migrations
|--------------------------------------------------------------------------
|
| Migrations are disabled by default for security reasons.
| You should enable migrations whenever you intend to do a schema migration
| and disable it back when you're done.
|
*/
$config['migration_enabled'] = FALSE;
/*
|--------------------------------------------------------------------------
| Migration Type
|--------------------------------------------------------------------------
|
| Migration file names may be based on a sequential identifier or on
| a timestamp. Options are:
|
| 'sequential' = Sequential migration naming (001_add_blog.php)
| 'timestamp' = Timestamp migration naming (20121031104401_add_blog.php)
| Use timestamp format YYYYMMDDHHIISS.
|
| Note: If this configuration value is missing the Migration library
| defaults to 'sequential' for backward compatibility with CI2.
|
*/
$config['migration_type'] = 'timestamp';
/*
|--------------------------------------------------------------------------
| Migrations table
|--------------------------------------------------------------------------
|
| This is the name of the table that will store the current migrations state.
| When migrations runs it will store in a database table which migration
| level the system is at. It then compares the migration level in this
| table to the $config['migration_version'] if they are not the same it
| will migrate up. This must be set.
|
*/
$config['migration_table'] = 'migrations';
/*
|--------------------------------------------------------------------------
| Auto Migrate To Latest
|--------------------------------------------------------------------------
|
| If this is set to TRUE when you load the migrations class and have
| $config['migration_enabled'] set to TRUE the system will auto migrate
| to your latest migration (whatever $config['migration_version'] is
| set to). This way you do not have to call migrations anywhere else
| in your code to have the latest migration.
|
*/
$config['migration_auto_latest'] = FALSE;
/*
|--------------------------------------------------------------------------
| Migrations version
|--------------------------------------------------------------------------
|
| This is used to set migration version that the file system should be on.
| If you run $this->migration->current() this is the version that schema will
| be upgraded / downgraded to.
|
*/
$config['migration_version'] = 0;
/*
|--------------------------------------------------------------------------
| Migrations Path
|--------------------------------------------------------------------------
|
| Path to your migrations folder.
| Typically, it will be within your application path.
| Also, writing permission is required within the migrations path.
|
*/
$config['migration_path'] = APPPATH.'migrations/';

View File

@@ -1,4 +1,6 @@
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
/*
| -------------------------------------------------------------------
| MIME TYPES
@@ -7,100 +9,159 @@
| Upload class to help identify allowed file types.
|
*/
$mimes = array( 'hqx' => 'application/mac-binhex40',
'cpt' => 'application/mac-compactpro',
'csv' => array('text/x-comma-separated-values', 'text/comma-separated-values', 'application/octet-stream', 'application/vnd.ms-excel', 'application/x-csv', 'text/x-csv', 'text/csv', 'application/csv', 'application/excel', 'application/vnd.msexcel'),
'bin' => 'application/macbinary',
'dms' => 'application/octet-stream',
'lha' => 'application/octet-stream',
'lzh' => 'application/octet-stream',
'exe' => array('application/octet-stream', 'application/x-msdownload'),
'class' => 'application/octet-stream',
'psd' => 'application/x-photoshop',
'so' => 'application/octet-stream',
'sea' => 'application/octet-stream',
'dll' => 'application/octet-stream',
'oda' => 'application/oda',
'pdf' => array('application/pdf', 'application/x-download'),
'ai' => 'application/postscript',
'eps' => 'application/postscript',
'ps' => 'application/postscript',
'smi' => 'application/smil',
'smil' => 'application/smil',
'mif' => 'application/vnd.mif',
'xls' => array('application/excel', 'application/vnd.ms-excel', 'application/msexcel'),
'ppt' => array('application/powerpoint', 'application/vnd.ms-powerpoint'),
'wbxml' => 'application/wbxml',
'wmlc' => 'application/wmlc',
'dcr' => 'application/x-director',
'dir' => 'application/x-director',
'dxr' => 'application/x-director',
'dvi' => 'application/x-dvi',
'gtar' => 'application/x-gtar',
'gz' => 'application/x-gzip',
'php' => 'application/x-httpd-php',
'php4' => 'application/x-httpd-php',
'php3' => 'application/x-httpd-php',
'phtml' => 'application/x-httpd-php',
'phps' => 'application/x-httpd-php-source',
'js' => 'application/x-javascript',
'swf' => 'application/x-shockwave-flash',
'sit' => 'application/x-stuffit',
'tar' => 'application/x-tar',
'tgz' => array('application/x-tar', 'application/x-gzip-compressed'),
'xhtml' => 'application/xhtml+xml',
'xht' => 'application/xhtml+xml',
'zip' => array('application/x-zip', 'application/zip', 'application/x-zip-compressed'),
'mid' => 'audio/midi',
'midi' => 'audio/midi',
'mpga' => 'audio/mpeg',
'mp2' => 'audio/mpeg',
'mp3' => array('audio/mpeg', 'audio/mpg', 'audio/mpeg3', 'audio/mp3'),
'aif' => 'audio/x-aiff',
'aiff' => 'audio/x-aiff',
'aifc' => 'audio/x-aiff',
'ram' => 'audio/x-pn-realaudio',
'rm' => 'audio/x-pn-realaudio',
'rpm' => 'audio/x-pn-realaudio-plugin',
'ra' => 'audio/x-realaudio',
'rv' => 'video/vnd.rn-realvideo',
'wav' => array('audio/x-wav', 'audio/wave', 'audio/wav'),
'bmp' => array('image/bmp', 'image/x-windows-bmp'),
'gif' => 'image/gif',
'jpeg' => array('image/jpeg', 'image/pjpeg'),
'jpg' => array('image/jpeg', 'image/pjpeg'),
'jpe' => array('image/jpeg', 'image/pjpeg'),
'png' => array('image/png', 'image/x-png'),
'tiff' => 'image/tiff',
'tif' => 'image/tiff',
'css' => 'text/css',
'html' => 'text/html',
'htm' => 'text/html',
'shtml' => 'text/html',
'txt' => 'text/plain',
'text' => 'text/plain',
'log' => array('text/plain', 'text/x-log'),
'rtx' => 'text/richtext',
'rtf' => 'text/rtf',
'xml' => 'text/xml',
'xsl' => 'text/xml',
'mpeg' => 'video/mpeg',
'mpg' => 'video/mpeg',
'mpe' => 'video/mpeg',
'qt' => 'video/quicktime',
'mov' => 'video/quicktime',
'avi' => 'video/x-msvideo',
'movie' => 'video/x-sgi-movie',
'doc' => 'application/msword',
'docx' => array('application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/zip'),
'xlsx' => array('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/zip'),
'word' => array('application/msword', 'application/octet-stream'),
'xl' => 'application/excel',
'eml' => 'message/rfc822',
'json' => array('application/json', 'text/json')
);
/* End of file mimes.php */
/* Location: ./application/config/mimes.php */
return array(
'hqx' => array('application/mac-binhex40', 'application/mac-binhex', 'application/x-binhex40', 'application/x-mac-binhex40'),
'cpt' => 'application/mac-compactpro',
'csv' => array('text/x-comma-separated-values', 'text/comma-separated-values', 'application/octet-stream', 'application/vnd.ms-excel', 'application/x-csv', 'text/x-csv', 'text/csv', 'application/csv', 'application/excel', 'application/vnd.msexcel', 'text/plain'),
'bin' => array('application/macbinary', 'application/mac-binary', 'application/octet-stream', 'application/x-binary', 'application/x-macbinary'),
'dms' => 'application/octet-stream',
'lha' => 'application/octet-stream',
'lzh' => 'application/octet-stream',
'exe' => array('application/octet-stream', 'application/x-msdownload'),
'class' => 'application/octet-stream',
'psd' => array('application/x-photoshop', 'image/vnd.adobe.photoshop'),
'so' => 'application/octet-stream',
'sea' => 'application/octet-stream',
'dll' => 'application/octet-stream',
'oda' => 'application/oda',
'pdf' => array('application/pdf', 'application/force-download', 'application/x-download', 'binary/octet-stream'),
'ai' => array('application/pdf', 'application/postscript'),
'eps' => 'application/postscript',
'ps' => 'application/postscript',
'smi' => 'application/smil',
'smil' => 'application/smil',
'mif' => 'application/vnd.mif',
'xls' => array('application/vnd.ms-excel', 'application/msexcel', 'application/x-msexcel', 'application/x-ms-excel', 'application/x-excel', 'application/x-dos_ms_excel', 'application/xls', 'application/x-xls', 'application/excel', 'application/download', 'application/vnd.ms-office', 'application/msword'),
'ppt' => array('application/powerpoint', 'application/vnd.ms-powerpoint', 'application/vnd.ms-office', 'application/msword'),
'pptx' => array('application/vnd.openxmlformats-officedocument.presentationml.presentation', 'application/x-zip', 'application/zip'),
'wbxml' => 'application/wbxml',
'wmlc' => 'application/wmlc',
'dcr' => 'application/x-director',
'dir' => 'application/x-director',
'dxr' => 'application/x-director',
'dvi' => 'application/x-dvi',
'gtar' => 'application/x-gtar',
'gz' => 'application/x-gzip',
'gzip' => 'application/x-gzip',
'php' => array('application/x-httpd-php', 'application/php', 'application/x-php', 'text/php', 'text/x-php', 'application/x-httpd-php-source'),
'php4' => 'application/x-httpd-php',
'php3' => 'application/x-httpd-php',
'phtml' => 'application/x-httpd-php',
'phps' => 'application/x-httpd-php-source',
'js' => array('application/x-javascript', 'text/plain'),
'swf' => 'application/x-shockwave-flash',
'sit' => 'application/x-stuffit',
'tar' => 'application/x-tar',
'tgz' => array('application/x-tar', 'application/x-gzip-compressed'),
'z' => 'application/x-compress',
'xhtml' => 'application/xhtml+xml',
'xht' => 'application/xhtml+xml',
'zip' => array('application/x-zip', 'application/zip', 'application/x-zip-compressed', 'application/s-compressed', 'multipart/x-zip'),
'rar' => array('application/x-rar', 'application/rar', 'application/x-rar-compressed'),
'mid' => 'audio/midi',
'midi' => 'audio/midi',
'mpga' => 'audio/mpeg',
'mp2' => 'audio/mpeg',
'mp3' => array('audio/mpeg', 'audio/mpg', 'audio/mpeg3', 'audio/mp3'),
'aif' => array('audio/x-aiff', 'audio/aiff'),
'aiff' => array('audio/x-aiff', 'audio/aiff'),
'aifc' => 'audio/x-aiff',
'ram' => 'audio/x-pn-realaudio',
'rm' => 'audio/x-pn-realaudio',
'rpm' => 'audio/x-pn-realaudio-plugin',
'ra' => 'audio/x-realaudio',
'rv' => 'video/vnd.rn-realvideo',
'wav' => array('audio/x-wav', 'audio/wave', 'audio/wav'),
'bmp' => array('image/bmp', 'image/x-bmp', 'image/x-bitmap', 'image/x-xbitmap', 'image/x-win-bitmap', 'image/x-windows-bmp', 'image/ms-bmp', 'image/x-ms-bmp', 'application/bmp', 'application/x-bmp', 'application/x-win-bitmap'),
'gif' => 'image/gif',
'jpeg' => array('image/jpeg', 'image/pjpeg'),
'jpg' => array('image/jpeg', 'image/pjpeg'),
'jpe' => array('image/jpeg', 'image/pjpeg'),
'jp2' => array('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),
'j2k' => array('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),
'jpf' => array('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),
'jpg2' => array('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),
'jpx' => array('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),
'jpm' => array('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),
'mj2' => array('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),
'mjp2' => array('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),
'png' => array('image/png', 'image/x-png'),
'tiff' => 'image/tiff',
'tif' => 'image/tiff',
'css' => array('text/css', 'text/plain'),
'html' => array('text/html', 'text/plain'),
'htm' => array('text/html', 'text/plain'),
'shtml' => array('text/html', 'text/plain'),
'txt' => 'text/plain',
'text' => 'text/plain',
'log' => array('text/plain', 'text/x-log'),
'rtx' => 'text/richtext',
'rtf' => 'text/rtf',
'xml' => array('application/xml', 'text/xml', 'text/plain'),
'xsl' => array('application/xml', 'text/xsl', 'text/xml'),
'mpeg' => 'video/mpeg',
'mpg' => 'video/mpeg',
'mpe' => 'video/mpeg',
'qt' => 'video/quicktime',
'mov' => 'video/quicktime',
'avi' => array('video/x-msvideo', 'video/msvideo', 'video/avi', 'application/x-troff-msvideo'),
'movie' => 'video/x-sgi-movie',
'doc' => array('application/msword', 'application/vnd.ms-office'),
'docx' => array('application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/zip', 'application/msword', 'application/x-zip'),
'dot' => array('application/msword', 'application/vnd.ms-office'),
'dotx' => array('application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/zip', 'application/msword'),
'xlsx' => array('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/zip', 'application/vnd.ms-excel', 'application/msword', 'application/x-zip'),
'word' => array('application/msword', 'application/octet-stream'),
'xl' => 'application/excel',
'eml' => 'message/rfc822',
'json' => array('application/json', 'text/json'),
'pem' => array('application/x-x509-user-cert', 'application/x-pem-file', 'application/octet-stream'),
'p10' => array('application/x-pkcs10', 'application/pkcs10'),
'p12' => 'application/x-pkcs12',
'p7a' => 'application/x-pkcs7-signature',
'p7c' => array('application/pkcs7-mime', 'application/x-pkcs7-mime'),
'p7m' => array('application/pkcs7-mime', 'application/x-pkcs7-mime'),
'p7r' => 'application/x-pkcs7-certreqresp',
'p7s' => 'application/pkcs7-signature',
'crt' => array('application/x-x509-ca-cert', 'application/x-x509-user-cert', 'application/pkix-cert'),
'crl' => array('application/pkix-crl', 'application/pkcs-crl'),
'der' => 'application/x-x509-ca-cert',
'kdb' => 'application/octet-stream',
'pgp' => 'application/pgp',
'gpg' => 'application/gpg-keys',
'sst' => 'application/octet-stream',
'csr' => 'application/octet-stream',
'rsa' => 'application/x-pkcs7',
'cer' => array('application/pkix-cert', 'application/x-x509-ca-cert'),
'3g2' => 'video/3gpp2',
'3gp' => array('video/3gp', 'video/3gpp'),
'mp4' => 'video/mp4',
'm4a' => 'audio/x-m4a',
'f4v' => array('video/mp4', 'video/x-f4v'),
'flv' => 'video/x-flv',
'webm' => 'video/webm',
'aac' => 'audio/x-acc',
'm4u' => 'application/vnd.mpegurl',
'm3u' => 'text/plain',
'xspf' => 'application/xspf+xml',
'vlc' => 'application/videolan',
'wmv' => array('video/x-ms-wmv', 'video/x-ms-asf'),
'au' => 'audio/x-au',
'ac3' => 'audio/ac3',
'flac' => 'audio/x-flac',
'ogg' => array('audio/ogg', 'video/ogg', 'application/ogg'),
'kmz' => array('application/vnd.google-earth.kmz', 'application/zip', 'application/x-zip'),
'kml' => array('application/vnd.google-earth.kml+xml', 'application/xml', 'text/xml'),
'ics' => 'text/calendar',
'ical' => 'text/calendar',
'zsh' => 'text/x-scriptzsh',
'7zip' => array('application/x-compressed', 'application/x-zip-compressed', 'application/zip', 'multipart/x-zip'),
'cdr' => array('application/cdr', 'application/coreldraw', 'application/x-cdr', 'application/x-coreldraw', 'image/cdr', 'image/x-cdr', 'zz-application/zz-winassoc-cdr'),
'wma' => array('audio/x-ms-wma', 'video/x-ms-asf'),
'jar' => array('application/java-archive', 'application/x-java-application', 'application/x-jar', 'application/x-compressed'),
'svg' => array('image/svg+xml', 'application/xml', 'text/xml'),
'vcf' => 'text/x-vcard',
'srt' => array('text/srt', 'text/plain'),
'vtt' => array('text/vtt', 'text/plain'),
'ico' => array('image/x-icon', 'image/x-ico', 'image/vnd.microsoft.icon')
);

View File

@@ -1,4 +1,6 @@
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
/*
| -------------------------------------------------------------------------
| Profiler Sections
@@ -7,11 +9,6 @@
| data are displayed when the Profiler is enabled.
| Please see the user guide for info:
|
| http://codeigniter.com/user_guide/general/profiling.html
| https://codeigniter.com/user_guide/general/profiling.html
|
*/
/* End of file profiler.php */
/* Location: ./application/config/profiler.php */

View File

@@ -1,4 +1,6 @@
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
/*
| -------------------------------------------------------------------------
| URI ROUTING
@@ -17,13 +19,13 @@
|
| Please see the user guide for complete details:
|
| http://codeigniter.com/user_guide/general/routing.html
| https://codeigniter.com/user_guide/general/routing.html
|
| -------------------------------------------------------------------------
| RESERVED ROUTES
| -------------------------------------------------------------------------
|
| There area two reserved routes:
| There are three reserved routes:
|
| $route['default_controller'] = 'welcome';
|
@@ -33,32 +35,44 @@
|
| $route['404_override'] = 'errors/page_missing';
|
| This route will tell the Router what URI segments to use if those provided
| in the URL cannot be matched to a valid route.
| This route will tell the Router which controller/method to use if those
| provided in the URL cannot be matched to a valid route.
|
| $route['translate_uri_dashes'] = FALSE;
|
| This is not exactly a route, but allows you to automatically route
| controller and method names that contain dashes. '-' isn't a valid
| class or method name character, so it requires translation.
| When you set this option to TRUE, it will replace ALL dashes in the
| controller and method URI segments.
|
| Examples: my-controller/index -> my_controller/index
| my-controller/my-method -> my_controller/my_method
*/
$route['default_controller'] = "login";
$route['no_access/(:any)'] = "no_access/index/$1";
$route['reports/(summary_:any)/(:any)/(:any)'] = "reports/$1/$2/$3";
$route['reports/summary_:any'] = "reports/date_input_excel_export";
$route['reports/(graphical_:any)/(:any)/(:any)'] = "reports/$1/$2/$3";
$route['reports/graphical_:any'] = "reports/date_input";
$route['reports/(inventory_:any)/(:any)'] = "reports/$1/$2";
$route['reports/inventory_:any'] = "reports/excel_export";
$route['default_controller'] = 'login';
$route['no_access/([^/]+)'] = 'no_access/index/$1';
$route['no_access/([^/]+)/([^/]+)'] = 'no_access/index/$1/$2';
$route['sales/index/([^/]+)'] = 'sales/manage/$1';
$route['sales/index/([^/]+)/([^/]+)'] = 'sales/manage/$1/$2';
$route['sales/index/([^/]+)/([^/]+)/([^/]+)'] = 'sales/manage/$1/$2/$3';
$route['reports/(summary_:any)/([^/]+)/([^/]+)'] = 'reports/$1/$2/$3';
$route['reports/summary_:any'] = 'reports/date_input_excel_export';
$route['reports/(graphical_:any)/([^/]+)/([^/]+)'] = 'reports/$1/$2/$3';
$route['reports/graphical_:any'] = 'reports/date_input';
$route['reports/(inventory_:any)/([^/]+)'] = 'reports/$1/$2';
$route['reports/inventory_:any'] = 'reports/excel_export';
$route['reports/inventory_summary'] = 'reports/inventory_summary_input';
$route['reports/(inventory_summary)/([^/]+)/([^/]+)/([^/]+)'] = 'reports/$1/$2/$3/$4';
$route['reports/(detailed_sales)/(:any)/(:any)'] = "reports/$1/$2/$3";
$route['reports/detailed_sales'] = "reports/date_input";
$route['reports/(detailed_receivings)/(:any)/(:any)'] = "reports/$1/$2/$3";
$route['reports/detailed_receivings'] = "reports/date_input_recv";
$route['reports/(specific_:any)/(:any)/(:any)/(:any)'] = "reports/$1/$2/$3/$4";
$route['reports/specific_customer'] = "reports/specific_customer_input";
$route['reports/specific_employee'] = "reports/specific_employee_input";
$route['reports/specific_discount'] = "reports/specific_discount_input";
$route['reports/(detailed_sales)/([^/]+)/([^/]+)/([^/]+)'] = 'reports/$1/$2/$3$/$4';
$route['reports/detailed_sales'] = 'reports/date_input_sales';
$route['reports/(detailed_receivings)/([^/]+)/([^/]+)/([^/]+)'] = 'reports/$1/$2/$3/$4';
$route['reports/detailed_receivings'] = 'reports/date_input_recv';
$route['reports/(specific_:any)/([^/]+)/([^/]+)/([^/]+)'] = 'reports/$1/$2/$3/$4';
$route['reports/specific_customer'] = 'reports/specific_customer_input';
$route['reports/specific_employee'] = 'reports/specific_employee_input';
$route['reports/specific_discount'] = 'reports/specific_discount_input';
$route['scaffolding_trigger'] = "";
$route['404_override'] = 'errors/page_missing';
/* End of file routes.php */
/* Location: ./application/config/routes.php */
$route['404_override'] = '';
$route['translate_uri_dashes'] = FALSE;

View File

@@ -1,17 +1,18 @@
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
/*
| -------------------------------------------------------------------
| SMILEYS
| -------------------------------------------------------------------
| This file contains an array of smileys for use with the emoticon helper.
| Individual images can be used to replace multiple simileys. For example:
| Individual images can be used to replace multiple smileys. For example:
| :-) and :) use the same image replacement.
|
| Please see user guide for more info:
| http://codeigniter.com/user_guide/helpers/smiley_helper.html
| https://codeigniter.com/user_guide/helpers/smiley_helper.html
|
*/
$smileys = array(
// smiley image name width height alt
@@ -57,10 +58,7 @@ $smileys = array(
':coolcheese:' => array('shade_cheese.gif', '19', '19', 'cool cheese'),
':vampire:' => array('vampire.gif', '19', '19', 'vampire'),
':snake:' => array('snake.gif', '19', '19', 'snake'),
':exclaim:' => array('exclaim.gif', '19', '19', 'excaim'),
':question:' => array('question.gif', '19', '19', 'question') // no comma after last item
':exclaim:' => array('exclaim.gif', '19', '19', 'exclaim'),
':question:' => array('question.gif', '19', '19', 'question')
);
/* End of file smileys.php */
/* Location: ./application/config/smileys.php */
);

View File

@@ -1,178 +1,211 @@
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
/*
| -------------------------------------------------------------------
| USER AGENT TYPES
| -------------------------------------------------------------------
| This file contains four arrays of user agent data. It is used by the
| This file contains four arrays of user agent data. It is used by the
| User Agent Class to help identify browser, platform, robot, and
| mobile device data. The array keys are used to identify the device
| mobile device data. The array keys are used to identify the device
| and the array values are used to set the actual name of the item.
|
*/
$platforms = array (
'windows nt 6.0' => 'Windows Longhorn',
'windows nt 5.2' => 'Windows 2003',
'windows nt 5.0' => 'Windows 2000',
'windows nt 5.1' => 'Windows XP',
'windows nt 4.0' => 'Windows NT 4.0',
'winnt4.0' => 'Windows NT 4.0',
'winnt 4.0' => 'Windows NT',
'winnt' => 'Windows NT',
'windows 98' => 'Windows 98',
'win98' => 'Windows 98',
'windows 95' => 'Windows 95',
'win95' => 'Windows 95',
'windows' => 'Unknown Windows OS',
'os x' => 'Mac OS X',
'ppc mac' => 'Power PC Mac',
'freebsd' => 'FreeBSD',
'ppc' => 'Macintosh',
'linux' => 'Linux',
'debian' => 'Debian',
'sunos' => 'Sun Solaris',
'beos' => 'BeOS',
'apachebench' => 'ApacheBench',
'aix' => 'AIX',
'irix' => 'Irix',
'osf' => 'DEC OSF',
'hp-ux' => 'HP-UX',
'netbsd' => 'NetBSD',
'bsdi' => 'BSDi',
'openbsd' => 'OpenBSD',
'gnu' => 'GNU/Linux',
'unix' => 'Unknown Unix OS'
);
$platforms = array(
'windows nt 10.0' => 'Windows 10',
'windows nt 6.3' => 'Windows 8.1',
'windows nt 6.2' => 'Windows 8',
'windows nt 6.1' => 'Windows 7',
'windows nt 6.0' => 'Windows Vista',
'windows nt 5.2' => 'Windows 2003',
'windows nt 5.1' => 'Windows XP',
'windows nt 5.0' => 'Windows 2000',
'windows nt 4.0' => 'Windows NT 4.0',
'winnt4.0' => 'Windows NT 4.0',
'winnt 4.0' => 'Windows NT',
'winnt' => 'Windows NT',
'windows 98' => 'Windows 98',
'win98' => 'Windows 98',
'windows 95' => 'Windows 95',
'win95' => 'Windows 95',
'windows phone' => 'Windows Phone',
'windows' => 'Unknown Windows OS',
'android' => 'Android',
'blackberry' => 'BlackBerry',
'iphone' => 'iOS',
'ipad' => 'iOS',
'ipod' => 'iOS',
'os x' => 'Mac OS X',
'ppc mac' => 'Power PC Mac',
'freebsd' => 'FreeBSD',
'ppc' => 'Macintosh',
'linux' => 'Linux',
'debian' => 'Debian',
'sunos' => 'Sun Solaris',
'beos' => 'BeOS',
'apachebench' => 'ApacheBench',
'aix' => 'AIX',
'irix' => 'Irix',
'osf' => 'DEC OSF',
'hp-ux' => 'HP-UX',
'netbsd' => 'NetBSD',
'bsdi' => 'BSDi',
'openbsd' => 'OpenBSD',
'gnu' => 'GNU/Linux',
'unix' => 'Unknown Unix OS',
'symbian' => 'Symbian OS'
);
// The order of this array should NOT be changed. Many browsers return
// multiple browser types so we want to identify the sub-type first.
$browsers = array(
'Flock' => 'Flock',
'Chrome' => 'Chrome',
'Opera' => 'Opera',
'MSIE' => 'Internet Explorer',
'Internet Explorer' => 'Internet Explorer',
'Shiira' => 'Shiira',
'Firefox' => 'Firefox',
'Chimera' => 'Chimera',
'Phoenix' => 'Phoenix',
'Firebird' => 'Firebird',
'Camino' => 'Camino',
'Netscape' => 'Netscape',
'OmniWeb' => 'OmniWeb',
'Safari' => 'Safari',
'Mozilla' => 'Mozilla',
'Konqueror' => 'Konqueror',
'icab' => 'iCab',
'Lynx' => 'Lynx',
'Links' => 'Links',
'hotjava' => 'HotJava',
'amaya' => 'Amaya',
'IBrowse' => 'IBrowse'
);
'OPR' => 'Opera',
'Flock' => 'Flock',
'Edge' => 'Spartan',
'Chrome' => 'Chrome',
// Opera 10+ always reports Opera/9.80 and appends Version/<real version> to the user agent string
'Opera.*?Version' => 'Opera',
'Opera' => 'Opera',
'MSIE' => 'Internet Explorer',
'Internet Explorer' => 'Internet Explorer',
'Trident.* rv' => 'Internet Explorer',
'Shiira' => 'Shiira',
'Firefox' => 'Firefox',
'Chimera' => 'Chimera',
'Phoenix' => 'Phoenix',
'Firebird' => 'Firebird',
'Camino' => 'Camino',
'Netscape' => 'Netscape',
'OmniWeb' => 'OmniWeb',
'Safari' => 'Safari',
'Mozilla' => 'Mozilla',
'Konqueror' => 'Konqueror',
'icab' => 'iCab',
'Lynx' => 'Lynx',
'Links' => 'Links',
'hotjava' => 'HotJava',
'amaya' => 'Amaya',
'IBrowse' => 'IBrowse',
'Maxthon' => 'Maxthon',
'Ubuntu' => 'Ubuntu Web Browser'
);
$mobiles = array(
// legacy array, old values commented out
'mobileexplorer' => 'Mobile Explorer',
// 'openwave' => 'Open Wave',
// 'opera mini' => 'Opera Mini',
// 'operamini' => 'Opera Mini',
// 'elaine' => 'Palm',
'palmsource' => 'Palm',
// 'digital paths' => 'Palm',
// 'avantgo' => 'Avantgo',
// 'xiino' => 'Xiino',
'palmscape' => 'Palmscape',
// 'nokia' => 'Nokia',
// 'ericsson' => 'Ericsson',
// 'blackberry' => 'BlackBerry',
// 'motorola' => 'Motorola'
// legacy array, old values commented out
'mobileexplorer' => 'Mobile Explorer',
// 'openwave' => 'Open Wave',
// 'opera mini' => 'Opera Mini',
// 'operamini' => 'Opera Mini',
// 'elaine' => 'Palm',
'palmsource' => 'Palm',
// 'digital paths' => 'Palm',
// 'avantgo' => 'Avantgo',
// 'xiino' => 'Xiino',
'palmscape' => 'Palmscape',
// 'nokia' => 'Nokia',
// 'ericsson' => 'Ericsson',
// 'blackberry' => 'BlackBerry',
// 'motorola' => 'Motorola'
// Phones and Manufacturers
'motorola' => "Motorola",
'nokia' => "Nokia",
'palm' => "Palm",
'iphone' => "Apple iPhone",
'ipad' => "iPad",
'ipod' => "Apple iPod Touch",
'sony' => "Sony Ericsson",
'ericsson' => "Sony Ericsson",
'blackberry' => "BlackBerry",
'cocoon' => "O2 Cocoon",
'blazer' => "Treo",
'lg' => "LG",
'amoi' => "Amoi",
'xda' => "XDA",
'mda' => "MDA",
'vario' => "Vario",
'htc' => "HTC",
'samsung' => "Samsung",
'sharp' => "Sharp",
'sie-' => "Siemens",
'alcatel' => "Alcatel",
'benq' => "BenQ",
'ipaq' => "HP iPaq",
'mot-' => "Motorola",
'playstation portable' => "PlayStation Portable",
'hiptop' => "Danger Hiptop",
'nec-' => "NEC",
'panasonic' => "Panasonic",
'philips' => "Philips",
'sagem' => "Sagem",
'sanyo' => "Sanyo",
'spv' => "SPV",
'zte' => "ZTE",
'sendo' => "Sendo",
// Phones and Manufacturers
'motorola' => 'Motorola',
'nokia' => 'Nokia',
'palm' => 'Palm',
'iphone' => 'Apple iPhone',
'ipad' => 'iPad',
'ipod' => 'Apple iPod Touch',
'sony' => 'Sony Ericsson',
'ericsson' => 'Sony Ericsson',
'blackberry' => 'BlackBerry',
'cocoon' => 'O2 Cocoon',
'blazer' => 'Treo',
'lg' => 'LG',
'amoi' => 'Amoi',
'xda' => 'XDA',
'mda' => 'MDA',
'vario' => 'Vario',
'htc' => 'HTC',
'samsung' => 'Samsung',
'sharp' => 'Sharp',
'sie-' => 'Siemens',
'alcatel' => 'Alcatel',
'benq' => 'BenQ',
'ipaq' => 'HP iPaq',
'mot-' => 'Motorola',
'playstation portable' => 'PlayStation Portable',
'playstation 3' => 'PlayStation 3',
'playstation vita' => 'PlayStation Vita',
'hiptop' => 'Danger Hiptop',
'nec-' => 'NEC',
'panasonic' => 'Panasonic',
'philips' => 'Philips',
'sagem' => 'Sagem',
'sanyo' => 'Sanyo',
'spv' => 'SPV',
'zte' => 'ZTE',
'sendo' => 'Sendo',
'nintendo dsi' => 'Nintendo DSi',
'nintendo ds' => 'Nintendo DS',
'nintendo 3ds' => 'Nintendo 3DS',
'wii' => 'Nintendo Wii',
'open web' => 'Open Web',
'openweb' => 'OpenWeb',
// Operating Systems
'symbian' => "Symbian",
'SymbianOS' => "SymbianOS",
'elaine' => "Palm",
'palm' => "Palm",
'series60' => "Symbian S60",
'windows ce' => "Windows CE",
// Operating Systems
'android' => 'Android',
'symbian' => 'Symbian',
'SymbianOS' => 'SymbianOS',
'elaine' => 'Palm',
'series60' => 'Symbian S60',
'windows ce' => 'Windows CE',
// Browsers
'obigo' => "Obigo",
'netfront' => "Netfront Browser",
'openwave' => "Openwave Browser",
'mobilexplorer' => "Mobile Explorer",
'operamini' => "Opera Mini",
'opera mini' => "Opera Mini",
// Browsers
'obigo' => 'Obigo',
'netfront' => 'Netfront Browser',
'openwave' => 'Openwave Browser',
'mobilexplorer' => 'Mobile Explorer',
'operamini' => 'Opera Mini',
'opera mini' => 'Opera Mini',
'opera mobi' => 'Opera Mobile',
'fennec' => 'Firefox Mobile',
// Other
'digital paths' => "Digital Paths",
'avantgo' => "AvantGo",
'xiino' => "Xiino",
'novarra' => "Novarra Transcoder",
'vodafone' => "Vodafone",
'docomo' => "NTT DoCoMo",
'o2' => "O2",
// Other
'digital paths' => 'Digital Paths',
'avantgo' => 'AvantGo',
'xiino' => 'Xiino',
'novarra' => 'Novarra Transcoder',
'vodafone' => 'Vodafone',
'docomo' => 'NTT DoCoMo',
'o2' => 'O2',
// Fallback
'mobile' => "Generic Mobile",
'wireless' => "Generic Mobile",
'j2me' => "Generic Mobile",
'midp' => "Generic Mobile",
'cldc' => "Generic Mobile",
'up.link' => "Generic Mobile",
'up.browser' => "Generic Mobile",
'smartphone' => "Generic Mobile",
'cellphone' => "Generic Mobile"
);
// Fallback
'mobile' => 'Generic Mobile',
'wireless' => 'Generic Mobile',
'j2me' => 'Generic Mobile',
'midp' => 'Generic Mobile',
'cldc' => 'Generic Mobile',
'up.link' => 'Generic Mobile',
'up.browser' => 'Generic Mobile',
'smartphone' => 'Generic Mobile',
'cellphone' => 'Generic Mobile'
);
// There are hundreds of bots but these are the most common.
$robots = array(
'googlebot' => 'Googlebot',
'msnbot' => 'MSNBot',
'slurp' => 'Inktomi Slurp',
'yahoo' => 'Yahoo',
'askjeeves' => 'AskJeeves',
'fastcrawler' => 'FastCrawler',
'infoseek' => 'InfoSeek Robot 1.0',
'lycos' => 'Lycos'
);
/* End of file user_agents.php */
/* Location: ./application/config/user_agents.php */
'googlebot' => 'Googlebot',
'msnbot' => 'MSNBot',
'baiduspider' => 'Baiduspider',
'bingbot' => 'Bing',
'slurp' => 'Inktomi Slurp',
'yahoo' => 'Yahoo',
'ask jeeves' => 'Ask Jeeves',
'fastcrawler' => 'FastCrawler',
'infoseek' => 'InfoSeek Robot 1.0',
'lycos' => 'Lycos',
'yandex' => 'YandexBot',
'mediapartners-google' => 'MediaPartners Google',
'CRAZYWEBCRAWLER' => 'Crazy Webcrawler',
'adsbot-google' => 'AdsBot Google',
'feedfetcher-google' => 'Feedfetcher Google',
'curious george' => 'Curious George'
);

View File

@@ -1,5 +1,6 @@
<?php
class Barcode extends CI_Controller
require_once ("Secure_area.php");
class Barcode extends Secure_area
{
function __construct()
{
@@ -10,5 +11,6 @@ class Barcode extends CI_Controller
{
$this->load->view('barcode');
}
}
?>

View File

@@ -0,0 +1,216 @@
<?php
require_once ("Secure_area.php");
class Config extends Secure_area
{
function __construct()
{
parent::__construct('config');
$this->load->library('barcode_lib');
}
function index()
{
$location_names = array();
$data['stock_locations'] = $this->Stock_location->get_all()->result_array();
$data['support_barcode'] = $this->barcode_lib->get_list_barcodes();
$this->load->view("configs/manage", $data);
}
function save()
{
$upload_success = $this->_handle_logo_upload();
$upload_data = $this->upload->data();
$batch_save_data=array(
'company'=>$this->input->post('company'),
'address'=>$this->input->post('address'),
'phone'=>$this->input->post('phone'),
'email'=>$this->input->post('email'),
'fax'=>$this->input->post('fax'),
'website'=>$this->input->post('website'),
'default_tax_1_rate'=>$this->input->post('default_tax_1_rate'),
'default_tax_1_name'=>$this->input->post('default_tax_1_name'),
'default_tax_2_rate'=>$this->input->post('default_tax_2_rate'),
'default_tax_2_name'=>$this->input->post('default_tax_2_name'),
'return_policy'=>$this->input->post('return_policy'),
'tax_included'=>$this->input->post('tax_included') != null,
'recv_invoice_format'=>$this->input->post('recv_invoice_format'),
'sales_invoice_format'=>$this->input->post('sales_invoice_format'),
'receiving_calculate_average_price'=>$this->input->post('receiving_calculate_average_price') != null,
'lines_per_page'=>$this->input->post('lines_per_page'),
'default_sales_discount'=>$this->input->post('default_sales_discount'),
'custom1_name'=>$this->input->post('custom1_name'),
'custom2_name'=>$this->input->post('custom2_name'),
'custom3_name'=>$this->input->post('custom3_name'),
'custom4_name'=>$this->input->post('custom4_name'),
'custom5_name'=>$this->input->post('custom5_name'),
'custom6_name'=>$this->input->post('custom6_name'),
'custom7_name'=>$this->input->post('custom7_name'),
'custom8_name'=>$this->input->post('custom8_name'),
'custom9_name'=>$this->input->post('custom9_name'),
'custom10_name'=>$this->input->post('custom10_name')
);
if (!empty($upload_data['orig_name']))
{
$batch_save_data['company_logo'] = $upload_data['raw_name'] . $upload_data['file_ext'];
}
$result = $this->Appconfig->batch_save( $batch_save_data );
$success = $upload_success && $result ? true : false;
$message = $this->lang->line('config_saved_' . ($success ? '' : 'un') . 'successfully');
$message = $upload_success ? $message : $this->upload->display_errors();
echo json_encode(array('success'=>$success,'message'=>$message));
}
function save_locale()
{
$batch_save_data=array(
'currency_symbol'=>$this->input->post('currency_symbol'),
'currency_side'=>$this->input->post('currency_side') != null,
'language'=>$this->input->post('language'),
'timezone'=>$this->input->post('timezone'),
'dateformat'=>$this->input->post('dateformat'),
'timeformat'=>$this->input->post('timeformat'),
'thousands_separator'=>$this->input->post('thousands_separator'),
'decimal_point'=>$this->input->post('decimal_point')
);
$result = $this->Appconfig->batch_save( $batch_save_data );
$success = $result ? true : false;
echo json_encode(array('success'=>$success, 'message'=>$this->lang->line('config_saved_' . ($success ? '' : 'un') . 'successfully')));
}
function stock_locations()
{
$stock_locations = $this->Stock_location->get_all()->result_array();
$this->load->view('partial/stock_locations', array('stock_locations' => $stock_locations));
}
function _clear_session_state()
{
$this->load->library('sale_lib');
$this->sale_lib->clear_sale_location();
$this->sale_lib->clear_all();
$this->load->library('receiving_lib');
$this->receiving_lib->clear_stock_source();
$this->receiving_lib->clear_stock_destination();
$this->receiving_lib->clear_all();
}
function save_locations()
{
$this->db->trans_start();
$deleted_locations = $this->Stock_location->get_allowed_locations();
foreach($this->input->post() as $key => $value)
{
if (strstr($key, 'stock_location'))
{
$location_id = preg_replace("/.*?_(\d+)$/", "$1", $key);
unset($deleted_locations[$location_id]);
// save or update
$location_data = array('location_name' => $value);
if ($this->Stock_location->save($location_data, $location_id))
{
$this->_clear_session_state();
}
}
}
// all locations not available in post will be deleted now
foreach ($deleted_locations as $location_id => $location_name)
{
$this->Stock_location->delete($location_id);
}
$success = $this->db->trans_complete();
echo json_encode(array('success'=>$success,'message'=>$this->lang->line('config_saved_' . ($success ? '' : 'un') . 'successfully')));
}
function save_barcode()
{
$batch_save_data=array(
'barcode_type'=>$this->input->post('barcode_type'),
'barcode_quality'=>$this->input->post('barcode_quality'),
'barcode_width'=>$this->input->post('barcode_width'),
'barcode_height'=>$this->input->post('barcode_height'),
'barcode_font'=>$this->input->post('barcode_font'),
'barcode_font_size'=>$this->input->post('barcode_font_size'),
'barcode_first_row'=>$this->input->post('barcode_first_row'),
'barcode_second_row'=>$this->input->post('barcode_second_row'),
'barcode_third_row'=>$this->input->post('barcode_third_row'),
'barcode_num_in_row'=>$this->input->post('barcode_num_in_row'),
'barcode_page_width'=>$this->input->post('barcode_page_width'),
'barcode_page_cellspacing'=>$this->input->post('barcode_page_cellspacing'),
'barcode_generate_if_empty'=>$this->input->post('barcode_generate_if_empty') != null,
'barcode_content'=>$this->input->post('barcode_content')
);
$result = $this->Appconfig->batch_save( $batch_save_data );
$success = $result ? true : false;
echo json_encode(array('success'=>$success, 'message'=>$this->lang->line('config_saved_' . ($success ? '' : 'un') . 'successfully')));
}
function save_receipt()
{
$batch_save_data = array (
'use_invoice_template' => $this->input->post ( 'use_invoice_template' ) != null,
'invoice_default_comments' => $this->input->post ( 'invoice_default_comments' ),
'invoice_email_message' => $this->input->post ( 'invoice_email_message' ),
'receipt_show_taxes' => $this->input->post ( 'receipt_show_taxes' ) != null,
'print_silently' => $this->input->post ( 'print_silently' ) != null,
'print_header' => $this->input->post ( 'print_header' ) != null,
'print_footer' => $this->input->post ( 'print_footer' ) != null,
'print_top_margin' => $this->input->post ( 'print_top_margin' ),
'print_left_margin' => $this->input->post ( 'print_left_margin' ),
'print_bottom_margin' => $this->input->post ( 'print_bottom_margin' ),
'print_right_margin' => $this->input->post ( 'print_right_margin' ),
'show_total_discount' => $this->input->post( 'show_total_discount' ) != null
);
$result = $this->Appconfig->batch_save( $batch_save_data );
$success = $result ? true : false;
echo json_encode(array('success'=>$success, 'message'=>$this->lang->line('config_saved_' . ($success ? '' : 'un') . 'successfully')));
}
function _handle_logo_upload()
{
$this->load->helper('directory');
// load upload library
$config = array('upload_path' => './uploads/',
'allowed_types' => 'gif|jpg|png',
'max_size' => '1024',
'max_width' => '800',
'max_height' => '680',
'file_name' => 'company_logo');
$this->load->library('upload', $config);
$this->upload->do_upload('company_logo');
return strlen($this->upload->display_errors()) == 0 || !strcmp($this->upload->display_errors(), '<p>'.$this->lang->line('upload_no_file_selected').'</p>');
}
function backup_db()
{
$employee_id=$this->Employee->get_logged_in_employee_info()->person_id;
if($this->Employee->has_module_grant('config',$employee_id))
{
$this->load->dbutil();
$prefs = array(
'format' => 'zip',
'filename' => 'ospos.sql'
);
$backup =& $this->dbutil->backup($prefs);
$file_name = 'ospos-' . date("Y-m-d-H-i-s") .'.zip';
$save = 'uploads/'.$file_name;
$this->load->helper('download');
while (ob_get_level()) {
ob_end_clean();
}
force_download($file_name, $backup);
}
else
{
redirect('no_access/config');
}
}
}
?>

View File

@@ -1,5 +1,6 @@
<?php
require_once ("person_controller.php");
require_once ("Person_controller.php");
class Customers extends Person_controller
{
function __construct()
@@ -7,18 +8,15 @@ class Customers extends Person_controller
parent::__construct('customers');
}
function index()
function index($limit_from=0)
{
$config['base_url'] = site_url('/customers/index');
$config['total_rows'] = $this->Customer->count_all();
$config['per_page'] = '20';
$config['uri_segment'] = 3;
$this->pagination->initialize($config);
$data['controller_name']=strtolower(get_class());
$data['form_width']=$this->get_form_width();
$data['manage_table']=get_people_manage_table( $this->Customer->get_all( $config['per_page'], $this->uri->segment( $config['uri_segment'] ) ), $this );
$this->load->view('people/manage',$data);
$data['controller_name'] = $this->get_controller_name();
$data['form_width'] = $this->get_form_width();
$lines_per_page = $this->Appconfig->get('lines_per_page');
$customers = $this->Customer->get_all($lines_per_page, $limit_from);
$data['links'] = $this->_initialize_pagination($this->Customer, $lines_per_page, $limit_from);
$data['manage_table'] = get_people_manage_table($customers, $this);
$this->load->view('people/manage', $data);
}
/*
@@ -26,9 +24,16 @@ class Customers extends Person_controller
*/
function search()
{
$search=$this->input->post('search');
$data_rows=get_people_manage_table_data_rows($this->Customer->search($search),$this);
echo $data_rows;
$search = $this->input->post('search') != '' ? $this->input->post('search') : null;
$limit_from = $this->input->post('limit_from');
$lines_per_page = $this->Appconfig->get('lines_per_page');
$customers = $this->Customer->search($search, $lines_per_page, $limit_from);
$total_rows = $this->Customer->get_found_rows($search);
$links = $this->_initialize_pagination($this->Customer,$lines_per_page, $limit_from, $total_rows);
$data_rows = get_people_manage_table_data_rows($customers, $this);
echo json_encode(array('total_rows' => $total_rows, 'rows' => $data_rows, 'pagination' => $links));
}
/*
@@ -55,23 +60,25 @@ class Customers extends Person_controller
function save($customer_id=-1)
{
$person_data = array(
'first_name'=>$this->input->post('first_name'),
'last_name'=>$this->input->post('last_name'),
'email'=>$this->input->post('email'),
'phone_number'=>$this->input->post('phone_number'),
'address_1'=>$this->input->post('address_1'),
'address_2'=>$this->input->post('address_2'),
'city'=>$this->input->post('city'),
'state'=>$this->input->post('state'),
'zip'=>$this->input->post('zip'),
'country'=>$this->input->post('country'),
'comments'=>$this->input->post('comments')
'first_name'=>$this->input->post('first_name'),
'last_name'=>$this->input->post('last_name'),
'gender'=>$this->input->post('gender'),
'email'=>$this->input->post('email'),
'phone_number'=>$this->input->post('phone_number'),
'address_1'=>$this->input->post('address_1'),
'address_2'=>$this->input->post('address_2'),
'city'=>$this->input->post('city'),
'state'=>$this->input->post('state'),
'zip'=>$this->input->post('zip'),
'country'=>$this->input->post('country'),
'comments'=>$this->input->post('comments')
);
$customer_data=array(
'account_number'=>$this->input->post('account_number')=='' ? null:$this->input->post('account_number'),
'taxable'=>$this->input->post('taxable')=='' ? 0:1,
'account_number'=>$this->input->post('account_number') == '' ? null : $this->input->post('account_number'),
'company_name'=>$this->input->post('company_name') == '' ? null : $this->input->post('company_name'),
'taxable'=>$this->input->post('taxable') != null
);
if($this->Customer->save($person_data,$customer_data,$customer_id))
if($this->Customer->save_customer($person_data,$customer_data,$customer_id))
{
//New customer
if($customer_id==-1)
@@ -92,6 +99,12 @@ class Customers extends Person_controller
}
}
function check_account_number()
{
$exists = $this->Customer->account_number_exists($this->input->post('account_number'),$this->input->post('person_id'));
echo !$exists ? 'true' : 'false';
}
/*
This deletes customers from the customers table
*/
@@ -145,23 +158,31 @@ class Customers extends Person_controller
$person_data = array(
'first_name'=>$data[0],
'last_name'=>$data[1],
'email'=>$data[2],
'phone_number'=>$data[3],
'address_1'=>$data[4],
'address_2'=>$data[5],
'city'=>$data[6],
'state'=>$data[7],
'zip'=>$data[8],
'country'=>$data[9],
'comments'=>$data[10]
'gender'=>$data[2],
'email'=>$data[3],
'phone_number'=>$data[4],
'address_1'=>$data[5],
'address_2'=>$data[6],
'city'=>$data[7],
'state'=>$data[8],
'zip'=>$data[9],
'country'=>$data[10],
'comments'=>$data[11]
);
$customer_data=array(
'account_number'=>$data[11]=='' ? null:$data[11],
'taxable'=>$data[12]=='' ? 0:1,
'taxable'=>$data[13]=='' ? 0:1
);
if(!$this->Customer->save($person_data,$customer_data))
$account_number = $data[12];
$invalidated = false;
if ($account_number != "")
{
$customer_data['account_number'] = $account_number;
$invalidated = $this->Customer->account_number_exists($account_number);
}
if($invalidated || !$this->Customer->save_customer($person_data,$customer_data))
{
$failCodes[] = $i;
}
@@ -177,7 +198,7 @@ class Customers extends Person_controller
}
$success = true;
if(count($failCodes) > 1)
if(count($failCodes) > 0)
{
$msg = "Most customers imported. But some were not, here is list of their CODE (" .count($failCodes) ."): ".implode(", ", $failCodes);
$success = false;
@@ -195,7 +216,7 @@ class Customers extends Person_controller
*/
function get_form_width()
{
return 350;
return 400;
}
}
?>

View File

@@ -1,5 +1,6 @@
<?php
require_once ("person_controller.php");
require_once ("Person_controller.php");
class Employees extends Person_controller
{
function __construct()
@@ -7,18 +8,15 @@ class Employees extends Person_controller
parent::__construct('employees');
}
function index()
function index($limit_from=0)
{
$config['base_url'] = site_url('/employees/index');
$config['total_rows'] = $this->Employee->count_all();
$config['per_page'] = '20';
$config['uri_segment'] = 3;
$this->pagination->initialize($config);
$data['controller_name']=strtolower(get_class());
$data['form_width']=$this->get_form_width();
$data['manage_table']=get_people_manage_table( $this->Employee->get_all( $config['per_page'], $this->uri->segment( $config['uri_segment'] ) ), $this );
$this->load->view('people/manage',$data);
$data['controller_name'] = $this->get_controller_name();
$data['form_width'] = $this->get_form_width();
$lines_per_page = $this->Appconfig->get('lines_per_page');
$suppliers = $this->Employee->get_all($lines_per_page, $limit_from);
$data['links'] = $this->_initialize_pagination($this->Employee, $lines_per_page, $limit_from);
$data['manage_table'] = get_people_manage_table($suppliers, $this);
$this->load->view('suppliers/manage',$data);
}
/*
@@ -26,9 +24,16 @@ class Employees extends Person_controller
*/
function search()
{
$search=$this->input->post('search');
$data_rows=get_people_manage_table_data_rows($this->Employee->search($search),$this);
echo $data_rows;
$search = $this->input->post('search') != '' ? $this->input->post('search') : null;
$limit_from = $this->input->post('limit_from');
$lines_per_page = $this->Appconfig->get('lines_per_page');
$employees = $this->Employee->search($search, $limit_from, $lines_per_page);
$total_rows = $this->Employee->get_found_rows($search);
$links = $this->_initialize_pagination($this->Employee, $lines_per_page, $limit_from, $total_rows);
$data_rows = get_people_manage_table_data_rows($employees, $this);
echo json_encode(array('rows' => $data_rows, 'pagination' => $links));
}
/*
@@ -47,6 +52,7 @@ class Employees extends Person_controller
{
$data['person_info']=$this->Employee->get_info($employee_id);
$data['all_modules']=$this->Module->get_all_modules();
$data['all_subpermissions']=$this->Module->get_all_subpermissions();
$this->load->view("employees/form",$data);
}
@@ -56,26 +62,27 @@ class Employees extends Person_controller
function save($employee_id=-1)
{
$person_data = array(
'first_name'=>$this->input->post('first_name'),
'last_name'=>$this->input->post('last_name'),
'email'=>$this->input->post('email'),
'phone_number'=>$this->input->post('phone_number'),
'address_1'=>$this->input->post('address_1'),
'address_2'=>$this->input->post('address_2'),
'city'=>$this->input->post('city'),
'state'=>$this->input->post('state'),
'zip'=>$this->input->post('zip'),
'country'=>$this->input->post('country'),
'comments'=>$this->input->post('comments')
'first_name'=>$this->input->post('first_name'),
'last_name'=>$this->input->post('last_name'),
'gender'=>$this->input->post('gender'),
'email'=>$this->input->post('email'),
'phone_number'=>$this->input->post('phone_number'),
'address_1'=>$this->input->post('address_1'),
'address_2'=>$this->input->post('address_2'),
'city'=>$this->input->post('city'),
'state'=>$this->input->post('state'),
'zip'=>$this->input->post('zip'),
'country'=>$this->input->post('country'),
'comments'=>$this->input->post('comments')
);
$permission_data = $this->input->post("permissions")!=false ? $this->input->post("permissions"):array();
$grants_data = $this->input->post('grants') != null ? $this->input->post('grants') : array();
//Password has been changed OR first time password set
if($this->input->post('password')!='')
if ( $this->input->post('password') != '' )
{
$employee_data=array(
'username'=>$this->input->post('username'),
'password'=>md5($this->input->post('password'))
'username'=>$this->input->post('username'),
'password'=>md5($this->input->post('password'))
);
}
else //Password not changed
@@ -83,7 +90,7 @@ class Employees extends Person_controller
$employee_data=array('username'=>$this->input->post('username'));
}
if($this->Employee->save($person_data,$employee_data,$permission_data,$employee_id))
if($this->Employee->save_employee($person_data,$employee_data,$grants_data,$employee_id))
{
//New employee
if($employee_id==-1)
@@ -126,7 +133,7 @@ class Employees extends Person_controller
*/
function get_form_width()
{
return 650;
return 750;
}
}
?>

View File

@@ -0,0 +1,129 @@
<?php
require_once ("Secure_area.php");
require_once ("interfaces/Idata_controller.php");
class Giftcards extends Secure_area implements iData_controller
{
function __construct()
{
parent::__construct('giftcards');
}
function index($limit_from=0)
{
$data['controller_name'] = $this->get_controller_name();
$data['form_width'] = $this->get_form_width();
$lines_per_page = $this->Appconfig->get('lines_per_page');
$giftcards = $this->Giftcard->get_all($lines_per_page, $limit_from);
$data['links'] = $this->_initialize_pagination($this->Giftcard, $lines_per_page, $limit_from);
$data['manage_table'] = get_giftcards_manage_table($giftcards, $this);
$this->load->view('giftcards/manage', $data);
}
/*
Returns Giftcards table data rows. This will be called with AJAX.
*/
function search()
{
$search = $this->input->post('search') != '' ? $this->input->post('search') : null;
$limit_from = $this->input->post('limit_from');
$lines_per_page = $this->Appconfig->get('lines_per_page');
$giftcards = $this->Giftcard->search($search, $lines_per_page, $limit_from);
$total_rows = $this->Giftcard->get_found_rows($search);
$links = $this->_initialize_pagination($this->Giftcard, $lines_per_page, $limit_from, $total_rows);
$data_rows = get_giftcards_manage_table_data_rows($giftcards, $this);
echo json_encode(array('total_rows' => $total_rows, 'rows' => $data_rows, 'pagination' => $links));
}
/*
Gives search suggestions based on what is being searched for
*/
function suggest()
{
$suggestions = $this->Giftcard->get_search_suggestions($this->input->post('q'), $this->input->post('limit'));
echo implode("\n",$suggestions);
}
/*
Gives search suggestions for person_id based on what is being searched for
*/
function person_search()
{
$suggestions = $this->Customer->get_customer_search_suggestions($this->input->post('q'), $this->input->post('limit'));
echo implode("\n",$suggestions);
}
function get_row()
{
$giftcard_id = $this->input->post('row_id');
$data_row = get_giftcard_data_row($this->Giftcard->get_info($giftcard_id), $this);
echo $data_row;
}
function view($giftcard_id=-1)
{
$giftcard_info = $this->Giftcard->get_info($giftcard_id);
$person_name=$giftcard_id > 0? $giftcard_info->first_name . ' ' . $giftcard_info->last_name : '';
$data['selected_person'] = $giftcard_id > 0 && isset($giftcard_info->person_id) ? $giftcard_info->person_id . "|" . $person_name : "";
$data['giftcard_number'] = $giftcard_id > 0 ? $giftcard_info->giftcard_number : $this->Giftcard->get_max_number()->giftcard_number + 1;
$data['giftcard_info'] = $giftcard_info;
$this->load->view("giftcards/form",$data);
}
function save($giftcard_id=-1)
{
$giftcard_data = array(
'record_time' => date('Y-m-d H:i:s'),
'giftcard_number'=>$this->input->post('giftcard_number', TRUE),
'value'=>$this->input->post('value', TRUE),
'person_id'=>$this->input->post('person_id', TRUE) ? $this->input->post('person_id') : null
);
if( $this->Giftcard->save( $giftcard_data, $giftcard_id ) )
{
//New giftcard
if($giftcard_id==-1)
{
echo json_encode(array('success'=>true, 'message'=>$this->lang->line('giftcards_successful_adding').' '.
$giftcard_data['giftcard_number'], 'giftcard_id'=>$giftcard_data['giftcard_id']));
$giftcard_id = $giftcard_data['giftcard_id'];
}
else //previous giftcard
{
echo json_encode(array('success'=>true, 'message'=>$this->lang->line('giftcards_successful_updating').' '.
$giftcard_data['giftcard_number'], 'giftcard_id'=>$giftcard_id));
}
}
else//failure
{
echo json_encode(array('success'=>false,'message'=>$this->lang->line('giftcards_error_adding_updating').' '.
$giftcard_data['giftcard_number'], 'giftcard_id'=>-1));
}
}
function delete()
{
$giftcards_to_delete=$this->input->post('ids');
if($this->Giftcard->delete_list($giftcards_to_delete))
{
echo json_encode(array('success'=>true, 'message'=>$this->lang->line('giftcards_successful_deleted').' '.
count($giftcards_to_delete).' '.$this->lang->line('giftcards_one_or_multiple')));
}
else
{
echo json_encode(array('success'=>false, 'message'=>$this->lang->line('giftcards_cannot_be_deleted')));
}
}
/*
get the width for the add/edit form
*/
function get_form_width()
{
return 360;
}
}
?>

View File

@@ -1,5 +1,5 @@
<?php
require_once ("secure_area.php");
require_once ("Secure_area.php");
class Home extends Secure_area
{

View File

@@ -0,0 +1,195 @@
<?php
require_once ("Secure_area.php");
require_once ("interfaces/Idata_controller.php");
class Item_kits extends Secure_area implements iData_controller
{
function __construct()
{
parent::__construct('item_kits');
}
// add the total cost and retail price to a passed items kit retrieving the data from each singolar item part of the kit
private function add_totals_to_item_kit($item_kit)
{
$item_kit->total_cost_price = 0;
$item_kit->total_unit_price = 0;
foreach ($this->Item_kit_items->get_info($item_kit->item_kit_id) as $item_kit_item)
{
$item_info = $this->Item->get_info($item_kit_item['item_id']);
$item_kit->total_cost_price += $item_info->cost_price * $item_kit_item['quantity'];
$item_kit->total_unit_price += $item_info->unit_price * $item_kit_item['quantity'];
}
return $item_kit;
}
function index($limit_from=0)
{
$data['controller_name'] = $this->get_controller_name();
$data['form_width'] = $this->get_form_width();
$lines_per_page = $this->Appconfig->get('lines_per_page');
$item_kits = $this->Item_kit->get_all($lines_per_page, $limit_from);
foreach($item_kits->result() as $item_kit)
{
// calculate the total cost and retail price of the Kit so it can be printed out in the manage table
$item_kit = $this->add_totals_to_item_kit($item_kit);
}
$data['links'] = $this->_initialize_pagination($this->Item_kit, $lines_per_page, $limit_from);
$data['manage_table'] = get_item_kits_manage_table($item_kits, $this);
$this->load->view('item_kits/manage', $data);
}
/*
Returns Item kits table data rows. This will be called with AJAX.
*/
function search()
{
$search = $this->input->post('search') != '' ? $this->input->post('search') : null;
$limit_from = $this->input->post('limit_from');
$lines_per_page = $this->Appconfig->get('lines_per_page');
$item_kits = $this->Item_kit->search($search, $lines_per_page, $limit_from);
$total_rows = $this->Item_kit->get_found_rows($search);
$links = $this->_initialize_pagination($this->Item_kit, $lines_per_page, $limit_from, $total_rows, 'search');
foreach($item_kits->result() as $item_kit)
{
// calculate the total cost and retail price of the Kit so it can be printed out in the manage table
$item_kit = $this->add_totals_to_item_kit($item_kit);
}
$data_rows = get_item_kits_manage_table_data_rows($item_kits, $this);
echo json_encode(array('total_rows' => $total_rows, 'rows' => $data_rows, 'pagination' => $links));
}
/*
Gives search suggestions based on what is being searched for
*/
function suggest()
{
$suggestions = $this->Item_kit->get_search_suggestions($this->input->post('q'), $this->input->post('limit'));
echo implode("\n", $suggestions);
}
function get_row()
{
$item_kit_id = $this->input->post('row_id');
// calculate the total cost and retail price of the Kit so it can be added to the table refresh
$item_kit = $this->add_totals_to_item_kit($this->Item_kit->get_info($item_kit_id));
echo (get_item_kit_data_row($item_kit, $this));
}
function view($item_kit_id=-1)
{
$data['item_kit_info'] = $this->Item_kit->get_info($item_kit_id);
$this->load->view("item_kits/form", $data);
}
function save($item_kit_id=-1)
{
$item_kit_data = array(
'name' => $this->input->post('name'),
'description' => $this->input->post('description')
);
if ($this->Item_kit->save($item_kit_data, $item_kit_id))
{
//New item kit
if ($item_kit_id==-1)
{
$item_kit_id = $item_kit_data['item_kit_id'];
echo json_encode(array('success'=>true,
'message'=>$this->lang->line('item_kits_successful_adding').' '.$item_kit_data['name'],
'item_kit_id'=>$item_kit_id));
}
else //previous item
{
echo json_encode(array('success'=>true,
'message'=>$this->lang->line('item_kits_successful_updating').' '.$item_kit_data['name'],
'item_kit_id'=>$item_kit_id));
}
if ( $this->input->post('item_kit_item') != null )
{
$item_kit_items = array();
foreach($this->input->post('item_kit_item') as $item_id => $quantity)
{
$item_kit_items[] = array(
'item_id' => $item_id,
'quantity' => $quantity
);
}
$this->Item_kit_items->save($item_kit_items, $item_kit_id);
}
}
else//failure
{
echo json_encode(array('success'=>false,
'message'=>$this->lang->line('item_kits_error_adding_updating').' '.$item_kit_data['name'],
'item_kit_id'=>-1));
}
}
function delete()
{
$item_kits_to_delete = $this->input->post('ids');
if ($this->Item_kit->delete_list($item_kits_to_delete))
{
echo json_encode(array('success'=>true,
'message'=>$this->lang->line('item_kits_successful_deleted').' '.count($item_kits_to_delete).' '.$this->lang->line('item_kits_one_or_multiple')));
}
else
{
echo json_encode(array('success'=>false,
'message'=>$this->lang->line('item_kits_cannot_be_deleted')));
}
}
function generate_barcodes($item_kit_ids)
{
$this->load->library('barcode_lib');
$result = array();
$item_kit_ids = explode(':', $item_kit_ids);
foreach ($item_kit_ids as $item_kid_id)
{
// calculate the total cost and retail price of the Kit so it can be added to the barcode text at the bottom
$item_kit = $this->add_totals_to_item_kit($this->Item_kit->get_info($item_kid_id));
$result[] = array('name'=>$item_kit->name, 'item_id'=>'KIT '.$item_kid_id, 'item_number'=>'KIT '.$item_kid_id, 'cost_price'=>$item_kit->total_cost_price, 'unit_price'=>$item_kit->total_unit_price);
}
$data['items'] = $result;
$barcode_config = $this->barcode_lib->get_barcode_config();
// in case the selected barcode type is not Code39 or Code128 we set by default Code128
// the rationale for this is that EAN codes cannot have strings as seed, so 'KIT ' is not allowed
if($barcode_config['barcode_type'] != 'Code39' && $barcode_config['barcode_type'] != 'Code128')
{
$barcode_config['barcode_type'] = 'Code128';
}
$data['barcode_config'] = $barcode_config;
$this->load->view("barcode_sheet", $data);
}
/*
get the width for the add/edit form
*/
function get_form_width()
{
return 400;
}
}
?>

View File

@@ -0,0 +1,800 @@
<?php
require_once ("Secure_area.php");
require_once ("interfaces/Idata_controller.php");
class Items extends Secure_area implements iData_controller
{
function __construct()
{
parent::__construct('items');
$this->load->library('item_lib');
}
function index($limit_from=0)
{
$stock_location = $this->item_lib->get_item_location();
$stock_locations = $this->Stock_location->get_allowed_locations();
$data['controller_name'] = $this->get_controller_name();
$data['form_width'] = $this->get_form_width();
$lines_per_page = $this->Appconfig->get('lines_per_page');
$items = $this->Item->get_all($stock_location, $lines_per_page, $limit_from);
$data['links'] = $this->_initialize_pagination($this->Item, $lines_per_page, $limit_from);
// set 01/01/2010 as starting date for OSPOS
$start_of_time = date($this->config->item('dateformat'), mktime(0,0,0,1,1,2010));
$today = date($this->config->item('dateformat'));
$start_date = $this->input->post('start_date') != null ? $this->input->post('start_date') : $start_of_time;
$start_date_formatter = date_create_from_format($this->config->item('dateformat'), $start_date);
$end_date = $this->input->post('end_date') != null ? $this->input->post('end_date') : $today;
$end_date_formatter = date_create_from_format($this->config->item('dateformat'), $end_date);
$data['start_date'] = $start_date_formatter->format($this->config->item('dateformat'));
$data['end_date'] = $end_date_formatter->format($this->config->item('dateformat'));
$data['stock_location'] = $stock_location;
$data['stock_locations'] = $stock_locations;
$data['manage_table'] = get_items_manage_table( $this->Item->get_all($stock_location, $lines_per_page, $limit_from), $this );
$this->load->view('items/manage', $data);
}
function find_item_info()
{
$item_number = $this->input->post('scan_item_number');
echo json_encode($this->Item->find_item_info($item_number));
}
/*
Returns Items table data rows. This will be called with AJAX.
*/
function search()
{
$search = $this->input->post('search') != '' ? $this->input->post('search') : null;
$limit_from = $this->input->post('limit_from');
$lines_per_page = $this->Appconfig->get('lines_per_page');
$this->item_lib->set_item_location($this->input->post('stock_location'));
// set 01/01/2010 as starting date for OSPOS
$start_of_time = date($this->config->item('dateformat'), mktime(0,0,0,1,1,2010));
$today = date($this->config->item('dateformat'));
$start_date = $this->input->post('start_date') != null ? $this->input->post('start_date') : $start_of_time;
$start_date_formatter = date_create_from_format($this->config->item('dateformat'), $start_date);
$end_date = $this->input->post('end_date') != null ? $this->input->post('end_date') : $today;
$end_date_formatter = date_create_from_format($this->config->item('dateformat'), $end_date);
$filters = array('start_date' => $start_date_formatter->format('Y-m-d'),
'end_date' => $end_date_formatter->format('Y-m-d'),
'stock_location_id' => $this->item_lib->get_item_location(),
'empty_upc' => $this->input->post('empty_upc') != null,
'low_inventory' => $this->input->post('low_inventory') != null,
'is_serialized' => $this->input->post('is_serialized') != null,
'no_description' => $this->input->post('no_description') != null,
'search_custom' => $this->input->post('search_custom') != null,
'is_deleted' => $this->input->post('is_deleted') != null);
$items = $this->Item->search($search, $filters, $lines_per_page, $limit_from);
$data_rows = get_items_manage_table_data_rows($items, $this);
$total_rows = $this->Item->get_found_rows($search, $filters);
$links = $this->_initialize_pagination($this->Item, $lines_per_page, $limit_from, $total_rows, 'search');
$data_rows = get_items_manage_table_data_rows($items, $this);
// do not move this line to be after the json_encode otherwise the searhc function won't work!!
echo json_encode(array('total_rows' => $total_rows, 'rows' => $data_rows, 'pagination' => $links));
}
function pic_thumb($pic_id)
{
$this->load->helper('file');
$this->load->library('image_lib');
$base_path = "uploads/item_pics/" . $pic_id ;
$images = glob ($base_path. "*");
if (sizeof($images) > 0)
{
$image_path = $images[0];
$ext = pathinfo($image_path, PATHINFO_EXTENSION);
$thumb_path = $base_path . $this->image_lib->thumb_marker.'.'.$ext;
if (sizeof($images) < 2)
{
$config['image_library'] = 'gd2';
$config['source_image'] = $image_path;
$config['maintain_ratio'] = TRUE;
$config['create_thumb'] = TRUE;
$config['width'] = 52;
$config['height'] = 32;
$this->image_lib->initialize($config);
$image = $this->image_lib->resize();
$thumb_path = $this->image_lib->full_dst_path;
}
$this->output->set_content_type(get_mime_by_extension($thumb_path));
$this->output->set_output(file_get_contents($thumb_path));
}
}
/*
Gives search suggestions based on what is being searched for
*/
function suggest()
{
$suggestions = $this->Item->get_search_suggestions($this->input->post('q'), $this->input->post('limit'),
$this->input->post('search_custom'), !empty($this->input->post('is_deleted')));
echo implode("\n",$suggestions);
}
function item_search()
{
$suggestions = $this->Item->get_item_search_suggestions($this->input->post('q'), $this->input->post('limit'));
echo implode("\n",$suggestions);
}
/*
Gives search suggestions based on what is being searched for
*/
function suggest_category()
{
$suggestions = $this->Item->get_category_suggestions($this->input->post('q'));
echo implode("\n",$suggestions);
}
/*
Gives search suggestions based on what is being searched for
*/
function suggest_location()
{
$suggestions = $this->Item->get_location_suggestions($this->input->post('q'));
echo implode("\n",$suggestions);
}
/*
Gives search suggestions based on what is being searched for
*/
function suggest_custom1()
{
$suggestions = $this->Item->get_custom1_suggestions($this->input->post('q'));
echo implode("\n",$suggestions);
}
/*
Gives search suggestions based on what is being searched for
*/
function suggest_custom2()
{
$suggestions = $this->Item->get_custom2_suggestions($this->input->post('q'));
echo implode("\n",$suggestions);
}
/*
Gives search suggestions based on what is being searched for
*/
function suggest_custom3()
{
$suggestions = $this->Item->get_custom3_suggestions($this->input->post('q'));
echo implode("\n",$suggestions);
}
/*
Gives search suggestions based on what is being searched for
*/
function suggest_custom4()
{
$suggestions = $this->Item->get_custom4_suggestions($this->input->post('q'));
echo implode("\n",$suggestions);
}
/*
Gives search suggestions based on what is being searched for
*/
function suggest_custom5()
{
$suggestions = $this->Item->get_custom5_suggestions($this->input->post('q'));
echo implode("\n",$suggestions);
}
/*
Gives search suggestions based on what is being searched for
*/
function suggest_custom6()
{
$suggestions = $this->Item->get_custom6_suggestions($this->input->post('q'));
echo implode("\n",$suggestions);
}
/*
Gives search suggestions based on what is being searched for
*/
function suggest_custom7()
{
$suggestions = $this->Item->get_custom7_suggestions($this->input->post('q'));
echo implode("\n",$suggestions);
}
/*
Gives search suggestions based on what is being searched for
*/
function suggest_custom8()
{
$suggestions = $this->Item->get_custom8_suggestions($this->input->post('q'));
echo implode("\n",$suggestions);
}
/*
Gives search suggestions based on what is being searched for
*/
function suggest_custom9()
{
$suggestions = $this->Item->get_custom9_suggestions($this->input->post('q'));
echo implode("\n",$suggestions);
}
/*
Gives search suggestions based on what is being searched for
*/
function suggest_custom10()
{
$suggestions = $this->Item->get_custom10_suggestions($this->input->post('q'));
echo implode("\n",$suggestions);
}
function get_row()
{
$item_id = $this->input->post('row_id');
$item_info = $this->Item->get_info($item_id);
$stock_location = $this->item_lib->get_item_location();
$item_quantity = $this->Item_quantity->get_item_quantity($item_id,$stock_location);
$item_info->quantity = $item_quantity->quantity;
$data_row = get_item_data_row($item_info,$this);
echo $data_row;
}
function view($item_id=-1)
{
$data['item_info']=$this->Item->get_info($item_id);
$data['item_tax_info']=$this->Item_taxes->get_info($item_id);
$suppliers = array('' => $this->lang->line('items_none'));
foreach($this->Supplier->get_all()->result_array() as $row)
{
$suppliers[$row['person_id']] = $row['company_name'];
}
$data['suppliers']=$suppliers;
$data['selected_supplier'] = $this->Item->get_info($item_id)->supplier_id;
$data['default_tax_1_rate']=($item_id==-1) ? $this->Appconfig->get('default_tax_1_rate') : '';
$data['default_tax_2_rate']=($item_id==-1) ? $this->Appconfig->get('default_tax_2_rate') : '';
$locations_data = $this->Stock_location->get_undeleted_all()->result_array();
foreach($locations_data as $location)
{
$quantity = $this->Item_quantity->get_item_quantity($item_id,$location['location_id'])->quantity;
$quantity = ($item_id == -1) ? null: $quantity;
$location_array[$location['location_id']] = array('location_name'=>$location['location_name'], 'quantity'=>$quantity);
$data['stock_locations'] = $location_array;
}
$this->load->view("items/form", $data);
}
function inventory($item_id=-1)
{
$data['item_info']=$this->Item->get_info($item_id);
$data['stock_locations'] = array();
$stock_locations = $this->Stock_location->get_undeleted_all()->result_array();
foreach($stock_locations as $location_data)
{
$data['stock_locations'][$location_data['location_id']] = $location_data['location_name'];
$data['item_quantities'][$location_data['location_id']] = $this->Item_quantity->get_item_quantity($item_id,$location_data['location_id'])->quantity;
}
$this->load->view("items/inventory", $data);
}
function count_details($item_id=-1)
{
$data['item_info']=$this->Item->get_info($item_id);
$data['stock_locations'] = array();
$stock_locations = $this->Stock_location->get_undeleted_all()->result_array();
foreach($stock_locations as $location_data)
{
$data['stock_locations'][$location_data['location_id']] = $location_data['location_name'];
$data['item_quantities'][$location_data['location_id']] = $this->Item_quantity->get_item_quantity($item_id,$location_data['location_id'])->quantity;
}
$this->load->view("items/count_details", $data);
}
function generate_barcodes($item_ids)
{
$this->load->library('barcode_lib');
$result = array();
$item_ids = explode(':', $item_ids);
$result = $this->Item->get_multiple_info($item_ids)->result_array();
$config = $this->barcode_lib->get_barcode_config();
$data['barcode_config'] = $config;
// check the list of items to see if any item_number field is empty
foreach($result as &$item)
{
// update the UPC/EAN/ISBN field if empty / null with the newly generated barcode
if (empty($item['item_number']) && $this->Appconfig->get('barcode_generate_if_empty'))
{
// get the newly generated barcode
$barcode_instance = Barcode_lib::barcode_instance($item, $config);
$item['item_number'] = $barcode_instance->getData();
// remove from item any suppliers table info to avoid save failure because of unknown fields
// WARNING: if suppliers table is changed this list needs to be upgraded, which makes the matter a bit tricky to maintain
unset($item['person_id']);
unset($item['company_name']);
unset($item['account_number']);
unset($item['agency_name']);
// update the item in the database in order to save the UPC/EAN/ISBN field
$this->Item->save($item, $item['item_id']);
}
}
$data['items'] = $result;
// display barcodes
$this->load->view("barcode_sheet", $data);
}
function bulk_edit()
{
$data = array();
$suppliers = array('' => $this->lang->line('items_none'));
foreach($this->Supplier->get_all()->result_array() as $row)
{
$suppliers[$row['person_id']] = $row['company_name'];
}
$data['suppliers'] = $suppliers;
$data['allow_alt_description_choices'] = array(
''=>$this->lang->line('items_do_nothing'),
1 =>$this->lang->line('items_change_all_to_allow_alt_desc'),
0 =>$this->lang->line('items_change_all_to_not_allow_allow_desc'));
$data['serialization_choices'] = array(
''=>$this->lang->line('items_do_nothing'),
1 =>$this->lang->line('items_change_all_to_serialized'),
0 =>$this->lang->line('items_change_all_to_unserialized'));
$this->load->view("items/form_bulk", $data);
}
function save($item_id=-1)
{
$upload_success = $this->_handle_image_upload();
$upload_data = $this->upload->data();
//Save item data
$item_data = array(
'name'=>$this->input->post('name'),
'description'=>$this->input->post('description'),
'category'=>$this->input->post('category'),
'supplier_id'=>$this->input->post('supplier_id') == '' ? null : $this->input->post('supplier_id'),
'item_number'=>$this->input->post('item_number') == '' ? null : $this->input->post('item_number'),
'cost_price'=>$this->input->post('cost_price'),
'unit_price'=>$this->input->post('unit_price'),
'reorder_level'=>$this->input->post('reorder_level'),
'receiving_quantity'=>$this->input->post('receiving_quantity'),
'allow_alt_description'=>$this->input->post('allow_alt_description') != null,
'is_serialized'=>$this->input->post('is_serialized') != null,
'deleted'=>$this->input->post('is_deleted') != null,
'custom1'=>$this->input->post('custom1') == null ? '' : $this->input->post('custom1'),
'custom2'=>$this->input->post('custom2') == null ? '' : $this->input->post('custom2'),
'custom3'=>$this->input->post('custom3') == null ? '' : $this->input->post('custom3'),
'custom4'=>$this->input->post('custom4') == null ? '' : $this->input->post('custom4'),
'custom5'=>$this->input->post('custom5') == null ? '' : $this->input->post('custom5'),
'custom6'=>$this->input->post('custom6') == null ? '' : $this->input->post('custom6'),
'custom7'=>$this->input->post('custom7') == null ? '' : $this->input->post('custom7'),
'custom8'=>$this->input->post('custom8') == null ? '' : $this->input->post('custom8'),
'custom9'=>$this->input->post('custom9') == null ? '' : $this->input->post('custom9'),
'custom10'=>$this->input->post('custom10') == null ? '' : $this->input->post('custom10')
);
if (!empty($upload_data['orig_name']))
{
$item_data['pic_id'] = $upload_data['raw_name'];
}
$employee_id = $this->Employee->get_logged_in_employee_info()->person_id;
$cur_item_info = $this->Item->get_info($item_id);
if($this->Item->save($item_data,$item_id))
{
$success = TRUE;
$new_item = FALSE;
//New item
if ($item_id==-1)
{
$item_id = $item_data['item_id'];
$new_item = TRUE;
}
$items_taxes_data = array();
$tax_names = $this->input->post('tax_names');
$tax_percents = $this->input->post('tax_percents');
for($k=0;$k<count($tax_percents);$k++)
{
if (is_numeric($tax_percents[$k]))
{
$items_taxes_data[] = array('name'=>$tax_names[$k], 'percent'=>$tax_percents[$k] );
}
}
$success &= $this->Item_taxes->save($items_taxes_data, $item_id);
//Save item quantity
$stock_locations = $this->Stock_location->get_undeleted_all()->result_array();
foreach($stock_locations as $location_data)
{
$updated_quantity = $this->input->post($location_data['location_id'].'_quantity');
$location_detail = array('item_id'=>$item_id,
'location_id'=>$location_data['location_id'],
'quantity'=>$updated_quantity);
$item_quantity = $this->Item_quantity->get_item_quantity($item_id, $location_data['location_id']);
if ($item_quantity->quantity != $updated_quantity || $new_item)
{
$success &= $this->Item_quantity->save($location_detail, $item_id, $location_data['location_id']);
$inv_data = array(
'trans_date'=>date('Y-m-d H:i:s'),
'trans_items'=>$item_id,
'trans_user'=>$employee_id,
'trans_location'=>$location_data['location_id'],
'trans_comment'=>$this->lang->line('items_manually_editing_of_quantity'),
'trans_inventory'=>$updated_quantity - $item_quantity->quantity
);
$success &= $this->Inventory->insert($inv_data);
}
}
if ($success && $upload_success)
{
$success_message = $this->lang->line('items_successful_' . ($new_item ? 'adding' : 'updating')) .' '. $item_data['name'];
echo json_encode(array('success'=>true, 'message'=>$success_message, 'item_id'=>$item_id));
}
else
{
$error_message = $upload_success ?
$this->lang->line('items_error_adding_updating') .' '. $item_data['name'] :
$this->upload->display_errors();
echo json_encode(array('success'=>false, 'message'=>$error_message, 'item_id'=>$item_id));
}
}
else//failure
{
echo json_encode(array('success'=>false, 'message'=>$this->lang->line('items_error_adding_updating').' '.$item_data['name'], 'item_id'=>-1));
}
}
function check_item_number()
{
$exists = $this->Item->item_number_exists($this->input->post('item_number'),$this->input->post('item_id'));
echo !$exists ? 'true' : 'false';
}
function _handle_image_upload()
{
$this->load->helper('directory');
$map = directory_map('./uploads/item_pics/', 1);
// load upload library
$config = array('upload_path' => './uploads/item_pics/',
'allowed_types' => 'gif|jpg|png',
'max_size' => '100',
'max_width' => '640',
'max_height' => '480',
'file_name' => sizeof($map));
$this->load->library('upload', $config);
$this->upload->do_upload('item_image');
return strlen($this->upload->display_errors()) == 0 ||
!strcmp($this->upload->display_errors(),
'<p>'.$this->lang->line('upload_no_file_selected').'</p>');
}
function save_inventory($item_id=-1)
{
$employee_id=$this->Employee->get_logged_in_employee_info()->person_id;
$cur_item_info = $this->Item->get_info($item_id);
$location_id = $this->input->post('stock_location');
$inv_data = array(
'trans_date'=>date('Y-m-d H:i:s'),
'trans_items'=>$item_id,
'trans_user'=>$employee_id,
'trans_location'=>$location_id,
'trans_comment'=>$this->input->post('trans_comment'),
'trans_inventory'=>$this->input->post('newquantity')
);
$this->Inventory->insert($inv_data);
//Update stock quantity
$item_quantity= $this->Item_quantity->get_item_quantity($item_id,$location_id);
$item_quantity_data = array(
'item_id'=>$item_id,
'location_id'=>$location_id,
'quantity'=>$item_quantity->quantity + $this->input->post('newquantity')
);
if($this->Item_quantity->save($item_quantity_data,$item_id,$location_id))
{
echo json_encode(array('success'=>true,'message'=>$this->lang->line('items_successful_updating').' '.
$cur_item_info->name,'item_id'=>$item_id));
}
else//failure
{
echo json_encode(array('success'=>false,'message'=>$this->lang->line('items_error_adding_updating').' '.
$cur_item_info->name,'item_id'=>-1));
}
}
function bulk_update()
{
$items_to_update=$this->input->post('item_ids');
$item_data = array();
foreach($_POST as $key=>$value)
{
//This field is nullable, so treat it differently
if($key == 'supplier_id' && $value != '')
{
$item_data["$key"] = $value;
}
elseif($value != '' && !(in_array($key, array('submit', 'item_ids', 'tax_names', 'tax_percents'))))
{
$item_data["$key"]=$value;
}
}
//Item data could be empty if tax information is being updated
if(empty($item_data) || $this->Item->update_multiple($item_data,$items_to_update))
{
$items_taxes_data = array();
$tax_names = $this->input->post('tax_names');
$tax_percents = $this->input->post('tax_percents');
$tax_updated = false;
for($k=0;$k<count($tax_percents);$k++)
{
if (!empty($tax_names[$k]) && is_numeric($tax_percents[$k]))
{
$tax_updated = true;
$items_taxes_data[] = array('name'=>$tax_names[$k], 'percent'=>$tax_percents[$k] );
}
}
if($tax_updated)
{
$this->Item_taxes->save_multiple($items_taxes_data, $items_to_update);
}
echo json_encode(array('success'=>true,'message'=>$this->lang->line('items_successful_bulk_edit')));
}
else
{
echo json_encode(array('success'=>false,'message'=>$this->lang->line('items_error_updating_multiple')));
}
}
function delete()
{
$items_to_delete=$this->input->post('ids');
if($this->Item->delete_list($items_to_delete))
{
echo json_encode(array('success'=>true,'message'=>$this->lang->line('items_successful_deleted').' '.
count($items_to_delete).' '.$this->lang->line('items_one_or_multiple')));
}
else
{
echo json_encode(array('success'=>false,'message'=>$this->lang->line('items_cannot_be_deleted')));
}
}
function excel()
{
$data = file_get_contents("import_items.csv");
$name = 'import_items.csv';
force_download($name, $data);
}
function excel_import()
{
$this->load->view("items/excel_import", null);
}
function do_excel_import()
{
$msg = 'do_excel_import';
$failCodes = array();
if ($_FILES['file_path']['error']!=UPLOAD_ERR_OK)
{
$msg = $this->lang->line('items_excel_import_failed');
echo json_encode( array('success'=>false,'message'=>$msg) );
return;
}
else
{
if (($handle = fopen($_FILES['file_path']['tmp_name'], "r")) !== FALSE)
{
//Skip first row
fgetcsv($handle);
$i=1;
while (($data = fgetcsv($handle)) !== FALSE)
{
if (sizeof($data) >= 23) {
$item_data = array(
'name' => $data[1],
'description' => $data[11],
'category' => $data[2],
'cost_price' => $data[4],
'unit_price' => $data[5],
'reorder_level' => $data[10],
'supplier_id' => $this->Supplier->exists($data[3]) ? $data[3] : null,
'allow_alt_description' => $data[12] != '' ? '1' : '0',
'is_serialized' => $data[13] != '' ? '1' : '0',
'custom1' => $data[14],
'custom2' => $data[15],
'custom3' => $data[16],
'custom4' => $data[17],
'custom5' => $data[18],
'custom6' => $data[19],
'custom7' => $data[20],
'custom8' => $data[21],
'custom9' => $data[22],
'custom10' => $data[23]
);
$item_number = $data[0];
$invalidated = false;
if ($item_number != "")
{
$item_data['item_number'] = $item_number;
$invalidated = $this->Item->item_number_exists($item_number);
}
}
else
{
$invalidated = true;
}
if(!$invalidated && $this->Item->save($item_data))
{
$items_taxes_data = null;
//tax 1
if( is_numeric($data[7]) && $data[6]!='' )
{
$items_taxes_data[] = array('name'=>$data[6], 'percent'=>$data[7] );
}
//tax 2
if( is_numeric($data[9]) && $data[8]!='' )
{
$items_taxes_data[] = array('name'=>$data[8], 'percent'=>$data[9] );
}
// save tax values
if(count($items_taxes_data) > 0)
{
$this->Item_taxes->save($items_taxes_data, $item_data['item_id']);
}
// quantities & inventory Info
$employee_id=$this->Employee->get_logged_in_employee_info()->person_id;
$emp_info=$this->Employee->get_info($employee_id);
$comment ='Qty CSV Imported';
$cols = count($data);
// array to store information if location got a quantity
$allowed_locations = $this->Stock_location->get_allowed_locations();
for ($col = 24; $col < $cols; $col = $col + 2)
{
$location_id = $data[$col];
if (array_key_exists($location_id, $allowed_locations))
{
$item_quantity_data = array (
'item_id' => $item_data['item_id'],
'location_id' => $location_id,
'quantity' => $data[$col + 1],
);
$this->Item_quantity->save($item_quantity_data, $item_data['item_id'], $location_id);
$excel_data = array(
'trans_items'=>$item_data['item_id'],
'trans_user'=>$employee_id,
'trans_comment'=>$comment,
'trans_location'=>$data[$col],
'trans_inventory'=>$data[$col + 1]
);
$this->Inventory->insert($excel_data);
unset($allowed_locations[$location_id]);
}
}
/*
* now iterate through the array and check for which location_id no entry into item_quantities was made yet
* those get an entry with quantity as 0.
* unfortunately a bit duplicate code from above...
*/
foreach($allowed_locations as $location_id => $location_name)
{
$item_quantity_data = array(
'item_id' => $item_data['item_id'],
'location_id' => $location_id,
'quantity' => 0,
);
$this->Item_quantity->save($item_quantity_data, $item_data['item_id'], $data[$col]);
$excel_data = array
(
'trans_items'=>$item_data['item_id'],
'trans_user'=>$employee_id,
'trans_comment'=>$comment,
'trans_location'=>$location_id,
'trans_inventory'=>0
);
$this->db->insert('inventory',$excel_data);
}
}
else//insert or update item failure
{
$failCodes[] = $i;
}
}
$i++;
}
else
{
echo json_encode( array('success'=>false, 'message'=>'Your upload file has no data or not in supported format.') );
return;
}
}
$success = true;
if(count($failCodes) > 0)
{
$msg = "Most items imported. But some were not, here is list of their CODE (" .count($failCodes) ."): ".implode(", ", $failCodes);
$success = false;
}
else
{
$msg = "Import items successful";
}
echo json_encode( array('success'=>$success, 'message'=>$msg) );
}
/*
get the width for the add/edit form
*/
function get_form_width()
{
return 450;
}
}
?>

View File

@@ -62,7 +62,7 @@ class Languagecheck extends CI_Controller {
foreach( $references as $reference )
{
// skip non-language files in the language directory
if ( strpos($reference, '_lang'.EXT) === FALSE )
if ( strpos($reference, '_lang.php') === FALSE )
{
continue;
}

View File

@@ -30,13 +30,15 @@ class Login extends CI_Controller
function login_check($username)
{
$password = $this->input->post("password");
$password = $this->input->post('password');
if(!$this->Employee->login($username,$password))
if(!$this->Employee->login($username, $password))
{
$this->form_validation->set_message('login_check', $this->lang->line('login_invalid_username_and_password'));
return false;
}
return true;
}
}

View File

@@ -6,9 +6,10 @@ class No_Access extends CI_Controller
parent::__construct();
}
function index($module_id='')
function index($module_id='',$permission_id='')
{
$data['module_name']=$this->Module->get_module_name($module_id);
$data['permission_id']=$permission_id;
$this->load->view('no_access',$data);
}
}

View File

@@ -1,6 +1,6 @@
<?php
require_once ("interfaces/iperson_controller.php");
require_once ("secure_area.php");
require_once ("interfaces/Iperson_controller.php");
require_once ("Secure_area.php");
abstract class Person_controller extends Secure_area implements iPerson_controller
{
function __construct($module_id=null)
@@ -30,15 +30,15 @@ abstract class Person_controller extends Secure_area implements iPerson_controll
}
echo '#';
}
/** GARRISON ADDED 4/25/2013 IN PROGRESS **/
/*
Gives search suggestions based on what is being searched for
*/
function suggest()
{
$suggestions = $this->Person->get_search_suggestions($this->input->post('q'),$this->input->post('limit'));
echo implode("\n",$suggestions);
}
/*
Gives search suggestions based on what is being searched for
*/
function suggest()
{
$suggestions = $this->Person->get_search_suggestions($this->input->post('q'),$this->input->post('limit'));
echo implode("\n",$suggestions);
}
/*
Gets one row for a person manage table. This is called using AJAX to update one row.
@@ -49,6 +49,5 @@ abstract class Person_controller extends Secure_area implements iPerson_controll
$data_row=get_person_data_row($this->Person->get_info($person_id),$this);
echo $data_row;
}
}
?>

View File

@@ -0,0 +1,411 @@
<?php
require_once ("Secure_area.php");
class Receivings extends Secure_area
{
function __construct()
{
parent::__construct('receivings');
$this->load->library('receiving_lib');
$this->load->library('barcode_lib');
}
function index()
{
$this->_reload();
}
function item_search()
{
$suggestions = $this->Item->get_item_search_suggestions($this->input->post('q'),$this->input->post('limit'));
$suggestions = array_merge($suggestions, $this->Item_kit->get_item_kit_search_suggestions($this->input->post('q'),$this->input->post('limit')));
echo implode("\n",$suggestions);
}
function supplier_search()
{
$suggestions = $this->Supplier->get_suppliers_search_suggestions($this->input->post('q'),$this->input->post('limit'));
echo implode("\n",$suggestions);
}
function select_supplier()
{
$supplier_id = $this->input->post('supplier');
if ($this->Supplier->exists($supplier_id))
{
$this->receiving_lib->set_supplier($supplier_id);
}
$this->_reload();
}
function change_mode()
{
$stock_destination = $this->input->post('stock_destination');
$stock_source = $this->input->post('stock_source');
if ((!$stock_source || $stock_source == $this->receiving_lib->get_stock_source()) &&
(!$stock_destination || $stock_destination == $this->receiving_lib->get_stock_destination()))
{
$this->receiving_lib->clear_invoice_number();
$mode = $this->input->post('mode');
$this->receiving_lib->set_mode($mode);
}
else if ($this->Stock_location->is_allowed_location($stock_source, 'receivings'))
{
$this->receiving_lib->set_stock_source($stock_source);
$this->receiving_lib->set_stock_destination($stock_destination);
}
$this->_reload();
}
function set_comment()
{
$this->receiving_lib->set_comment($this->input->post('comment'));
}
function set_invoice_number_enabled()
{
$this->receiving_lib->set_invoice_number_enabled($this->input->post('recv_invoice_number_enabled'));
}
function set_print_after_sale()
{
$this->receiving_lib->set_print_after_sale($this->input->post('recv_print_after_sale'));
}
function set_invoice_number()
{
$this->receiving_lib->set_invoice_number($this->input->post('recv_invoice_number'));
}
function add()
{
$data=array();
$mode = $this->receiving_lib->get_mode();
$item_id_or_number_or_item_kit_or_receipt = $this->input->post('item');
$quantity = ($mode=="receive" or $mode=="requisition") ? 1:-1;
$item_location = $this->receiving_lib->get_stock_source();
if($mode=='return' && $this->receiving_lib->is_valid_receipt($item_id_or_number_or_item_kit_or_receipt))
{
$this->receiving_lib->return_entire_receiving($item_id_or_number_or_item_kit_or_receipt);
}
elseif($this->receiving_lib->is_valid_item_kit($item_id_or_number_or_item_kit_or_receipt))
{
$this->receiving_lib->add_item_kit($item_id_or_number_or_item_kit_or_receipt,$item_location);
}
else
{
if(!$this->receiving_lib->add_item($item_id_or_number_or_item_kit_or_receipt,$quantity,$item_location))
$data['error']=$this->lang->line('recvs_unable_to_add_item');
}
$this->_reload($data);
}
function edit_item($item_id)
{
$data= array();
$this->form_validation->set_rules('price', 'lang:items_price', 'required|numeric');
$this->form_validation->set_rules('quantity', 'lang:items_quantity', 'required|numeric');
$this->form_validation->set_rules('discount', 'lang:items_discount', 'required|numeric');
$description = $this->input->post('description');
$serialnumber = $this->input->post('serialnumber');
$price = $this->input->post('price');
$quantity = $this->input->post('quantity');
$discount = $this->input->post('discount');
$item_location = $this->input->post('location');
if ($this->form_validation->run() != FALSE)
{
$this->receiving_lib->edit_item($item_id,$description,$serialnumber,$quantity,$discount,$price);
}
else
{
$data['error']=$this->lang->line('recvs_error_editing_item');
}
$this->_reload($data);
}
function edit($receiving_id)
{
$data = array();
$data['suppliers'] = array('' => 'No Supplier');
foreach ($this->Supplier->get_all()->result() as $supplier)
{
$data['suppliers'][$supplier->person_id] = $supplier->first_name . ' ' . $supplier->last_name;
}
$data['employees'] = array();
foreach ($this->Employee->get_all()->result() as $employee)
{
$data['employees'][$employee->person_id] = $employee->first_name . ' '. $employee->last_name;
}
$receiving_info = $this->Receiving->get_info($receiving_id)->row_array();
$person_name = $receiving_info['first_name'] . " " . $receiving_info['last_name'];
$data['selected_supplier'] = !empty($receiving_info['supplier_id']) ? $receiving_info['supplier_id'] . "|" . $person_name : "";
$data['receiving_info'] = $receiving_info;
$this->load->view('receivings/form', $data);
}
function delete_item($item_number)
{
$this->receiving_lib->delete_item($item_number);
$this->_reload();
}
function delete($receiving_id = -1, $update_inventory=TRUE)
{
$employee_id=$this->Employee->get_logged_in_employee_info()->person_id;
$receiving_ids=$receiving_id == -1 ? $this->input->post('ids') : array($receiving_id);
if($this->Receiving->delete_list($receiving_ids, $employee_id, $update_inventory))
{
echo json_encode(array('success'=>true,'message'=>$this->lang->line('recvs_successfully_deleted').' '.
count($receiving_ids).' '.$this->lang->line('recvs_one_or_multiple'),'ids'=>$receiving_ids));
}
else
{
echo json_encode(array('success'=>false,'message'=>$this->lang->line('recvs_cannot_be_deleted')));
}
}
function delete_supplier()
{
$this->receiving_lib->clear_invoice_number();
$this->receiving_lib->delete_supplier();
$this->_reload();
}
function complete()
{
$data['cart']=$this->receiving_lib->get_cart();
$data['total']=$this->receiving_lib->get_total();
$data['receipt_title']=$this->lang->line('recvs_receipt');
$data['transaction_time']= date($this->config->item('dateformat').' '.$this->config->item('timeformat'));
$data['mode']=$this->receiving_lib->get_mode();
$data['show_stock_locations']=$this->Stock_location->show_locations('receivings');
$supplier_id=$this->receiving_lib->get_supplier();
$employee_id=$this->Employee->get_logged_in_employee_info()->person_id;
$comment = $this->input->post('comment');
$emp_info=$this->Employee->get_info($employee_id);
$payment_type=$this->input->post('payment_type');
$data['stock_location']=$this->receiving_lib->get_stock_source();
if ( $this->input->post('amount_tendered') != null )
{
$data['amount_tendered'] = $this->input->post('amount_tendered');
$data['amount_change'] = to_currency($data['amount_tendered'] - round($data['total'], 2));
}
$data['employee']=$emp_info->first_name.' '.$emp_info->last_name;
$suppl_info ='';
if($supplier_id!=-1)
{
$suppl_info=$this->Supplier->get_info($supplier_id);
$data['supplier']=$suppl_info->company_name; // first_name.' '.$suppl_info->last_name;
}
$invoice_number=$this->_substitute_invoice_number($suppl_info);
if ($this->receiving_lib->is_invoice_number_enabled() && $this->Receiving->invoice_number_exists($invoice_number))
{
$data['error']=$this->lang->line('recvs_invoice_number_duplicate');
$this->_reload($data);
}
else
{
$invoice_number = $this->receiving_lib->is_invoice_number_enabled() ? $invoice_number : null;
$data['invoice_number']=$invoice_number;
$data['payment_type']=$this->input->post('payment_type');
//SAVE receiving to database
$data['receiving_id']='RECV '.$this->Receiving->save($data['cart'], $supplier_id,$employee_id,$comment,$invoice_number,$payment_type,$data['stock_location']);
if ($data['receiving_id'] == 'RECV -1')
{
$data['error_message'] = $this->lang->line('receivings_transaction_failed');
}
$data['barcode']=$this->barcode_lib->generate_receipt_barcode($data['receiving_id']);
$data['print_after_sale'] = $this->receiving_lib->is_print_after_sale();
$this->load->view("receivings/receipt",$data);
$this->receiving_lib->clear_all();
}
}
private function _substitute_variable($text, $variable, $object, $function)
{
// don't query if this variable isn't used
if (strstr($text, $variable))
{
$value = call_user_func(array($object, $function));
$text = str_replace($variable, $value, $text);
}
return $text;
}
private function _substitute_variables($text,$supplier_info)
{
$text=$this->_substitute_variable($text, '$YCO', $this->Receiving, 'get_invoice_number_for_year');
$text=$this->_substitute_variable($text, '$CO', $this->Receiving , 'get_invoice_count');
$text=strftime($text);
$text=$this->_substitute_supplier($text, $supplier_info);
return $text;
}
private function _substitute_supplier($text,$supplier_info)
{
$supplier_id=$this->receiving_lib->get_supplier();
if($supplier_id!=-1)
{
$text=str_replace('$SU',$supplier_info->company_name,$text);
$words = preg_split("/\s+/", trim($supplier_info->company_name));
$acronym = "";
foreach ($words as $w) {
$acronym .= $w[0];
}
$text=str_replace('$SI',$acronym,$text);
}
return $text;
}
private function _substitute_invoice_number($supplier_info='')
{
$invoice_number=$this->receiving_lib->get_invoice_number();
$invoice_number=$this->config->config['recv_invoice_format'];
$invoice_number = $this->_substitute_variables($invoice_number,$supplier_info);
$this->receiving_lib->set_invoice_number($invoice_number);
return $this->receiving_lib->get_invoice_number();
}
function requisition_complete()
{
if ($this->receiving_lib->get_stock_source() != $this->receiving_lib->get_stock_destination())
{
foreach($this->receiving_lib->get_cart() as $item)
{
$this->receiving_lib->delete_item($item['line']);
$this->receiving_lib->add_item($item['item_id'],$item['quantity'],$this->receiving_lib->get_stock_destination());
$this->receiving_lib->add_item($item['item_id'],-$item['quantity'],$this->receiving_lib->get_stock_source());
}
$this->complete();
}
else
{
$data['error']=$this->lang->line('recvs_error_requisition');
$this->_reload($data);
}
}
function receipt($receiving_id)
{
$receiving_info = $this->Receiving->get_info($receiving_id)->row_array();
$this->receiving_lib->copy_entire_receiving($receiving_id);
$data['cart']=$this->receiving_lib->get_cart();
$data['total']=$this->receiving_lib->get_total();
$data['mode']=$this->receiving_lib->get_mode();
$data['receipt_title']=$this->lang->line('recvs_receipt');
$data['transaction_time']= date($this->config->item('dateformat').' '.$this->config->item('timeformat'), strtotime($receiving_info['receiving_time']));
$data['show_stock_locations']=$this->Stock_location->show_locations('receivings');
$supplier_id=$this->receiving_lib->get_supplier();
$emp_info=$this->Employee->get_info($receiving_info['employee_id']);
$data['payment_type']=$receiving_info['payment_type'];
$data['invoice_number']=$this->receiving_lib->get_invoice_number();
$data['receiving_id']='RECV '.$receiving_id;
$data['barcode']=$this->barcode_lib->generate_receipt_barcode($data['receiving_id']);
$data['employee']=$emp_info->first_name.' '.$emp_info->last_name;
if($supplier_id!=-1)
{
$supplier_info=$this->Supplier->get_info($supplier_id);
$data['supplier']=$supplier_info->first_name.' '.$supplier_info->last_name;
}
$data['print_after_sale'] = FALSE;
$this->load->view("receivings/receipt",$data);
$this->receiving_lib->clear_all();
}
private function _reload($data=array())
{
$person_info = $this->Employee->get_logged_in_employee_info();
$data['cart']=$this->receiving_lib->get_cart();
$data['modes']=array('receive'=>$this->lang->line('recvs_receiving'),'return'=>$this->lang->line('recvs_return'));
$data['mode']=$this->receiving_lib->get_mode();
$data['stock_locations']=$this->Stock_location->get_allowed_locations('receivings');
$show_stock_locations = count($data['stock_locations']) > 1;
if ($show_stock_locations)
{
$data['modes']['requisition']=$this->lang->line('recvs_requisition');
$data['stock_source']=$this->receiving_lib->get_stock_source();
$data['stock_destination']=$this->receiving_lib->get_stock_destination();
}
$data['show_stock_locations']=$show_stock_locations;
$data['total']=$this->receiving_lib->get_total();
$data['items_module_allowed']=$this->Employee->has_grant('items',$person_info->person_id);
$data['comment']=$this->receiving_lib->get_comment();
$data['payment_options']=array(
$this->lang->line('sales_cash') => $this->lang->line('sales_cash'),
$this->lang->line('sales_check') => $this->lang->line('sales_check'),
$this->lang->line('sales_debit') => $this->lang->line('sales_debit'),
$this->lang->line('sales_credit') => $this->lang->line('sales_credit')
);
$supplier_id=$this->receiving_lib->get_supplier();
$suppl_info='';
if($supplier_id!=-1)
{
$suppl_info=$this->Supplier->get_info($supplier_id);
$data['supplier']=$suppl_info->company_name; // first_name.' '.$info->last_name;
}
$data['invoice_number']=$this->_substitute_invoice_number($suppl_info);
$data['invoice_number_enabled']=$this->receiving_lib->is_invoice_number_enabled();
$data['print_after_sale']=$this->receiving_lib->is_print_after_sale();
$this->load->view("receivings/receiving",$data);
}
function save($receiving_id)
{
$date_formatter = date_create_from_format($this->config->item('dateformat') . ' ' . $this->config->item('timeformat'), $this->input->post('date', TRUE));
$receiving_data = array(
'receiving_time' => $date_formatter->format('Y-m-d H:i:s'),
'supplier_id' => empty($this->input->post('supplier_id')) ? NULL : $this->input->post('supplier_id'),
'employee_id' => $this->input->post('employee_id'),
'comment' => $this->input->post('comment'),
'invoice_number' => $this->input->post('invoice_number')
);
if ($this->Receiving->update($receiving_data, $receiving_id))
{
echo json_encode(array(
'success'=>true,
'message'=>$this->lang->line('recvs_successfully_updated'),
'id'=>$receiving_id)
);
}
else
{
echo json_encode(array(
'success'=>false,
'message'=>$this->lang->line('recvs_unsuccessfully_updated'),
'id'=>$receiving_id)
);
}
}
function cancel_receiving()
{
$this->receiving_lib->clear_all();
$this->_reload();
}
function check_invoice_number()
{
$receiving_id=$this->input->post('receiving_id');
$invoice_number=$this->input->post('invoice_number');
$exists=!empty($invoice_number) && $this->Receiving->invoice_number_exists($invoice_number,$receiving_id);
echo !$exists ? 'true' : 'false';
}
}
?>

View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,848 @@
<?php
require_once ("Secure_area.php");
class Sales extends Secure_area
{
function __construct()
{
parent::__construct('sales');
$this->load->library('sale_lib');
$this->load->library('barcode_lib');
}
function index()
{
$this->_reload();
}
function manage($only_invoices = FALSE, $only_cash = FALSE, $limit_from = 0)
{
$person_id = $this->session->userdata('person_id');
if (!$this->Employee->has_grant('reports_sales', $person_id))
{
redirect('no_access/sales/reports_sales');
}
else
{
$this->Sale->create_sales_items_temp_table();
$data['controller_name'] = $this->get_controller_name();
$lines_per_page = $this->Appconfig->get('lines_per_page');
$today = date($this->config->item('dateformat'));
$start_date = $this->input->post('start_date') != null ? $this->input->post('start_date') : $today;
$start_date_formatter = date_create_from_format($this->config->item('dateformat'), $start_date);
$end_date = $this->input->post('end_date') != null ? $this->input->post('end_date') : $today;
$end_date_formatter = date_create_from_format($this->config->item('dateformat'), $end_date);
$sale_type = 'all';
$location_id = 'all';
$is_valid_receipt = FALSE;
$search = null;
$filters = array('sale_type' => $sale_type,
'location_id' => $location_id,
'start_date' => $start_date_formatter->format('Y-m-d'),
'end_date' => $end_date_formatter->format('Y-m-d'),
'only_invoices' => $only_invoices,
'only_cash' => $only_cash,
'is_valid_receipt' => $is_valid_receipt);
$sales = $this->Sale->search($search, $filters, $lines_per_page, $limit_from)->result_array();
$payments = $this->Sale->get_payments_summary($search, $filters);
$total_rows = $this->Sale->get_found_rows($search, $filters);
$data['only_invoices'] = $only_invoices;
$data['only_cash '] = $only_cash;
$data['start_date'] = $start_date_formatter->format($this->config->item('dateformat'));
$data['end_date'] = $end_date_formatter->format($this->config->item('dateformat'));
$data['links'] = $this->_initialize_pagination($this->Sale, $lines_per_page, $limit_from, $total_rows, 'manage', $only_invoices);
$data['manage_table'] = get_sales_manage_table($sales, $this);
$data['payments_summary'] = get_sales_manage_payments_summary($payments, $sales, $this);
$this->load->view($data['controller_name'] . '/manage', $data);
}
}
function get_row()
{
$this->Sale->create_sales_items_temp_table();
$sale_id = $this->input->post('row_id');
$sale_info = $this->Sale->get_info($sale_id)->result_array();
$data_row = get_sales_manage_sale_data_row($sale_info[0], $this);
echo $data_row;
}
/**
*
* Get the width for the add/edit form.
* @return number The form width
*/
function get_form_width()
{
return 400;
}
/*
Returns Sales table data rows. This will be called with AJAX.
*/
function search()
{
$this->Sale->create_sales_items_temp_table();
$search = $this->input->post('search') != '' ? $this->input->post('search') : null;
$limit_from = $this->input->post('limit_from');
$lines_per_page = $this->Appconfig->get('lines_per_page');
$today = date($this->config->item('dateformat'));
$start_date = $this->input->post('start_date') != null ? $this->input->post('start_date') : $today;
$start_date_formatter = date_create_from_format($this->config->item('dateformat'), $start_date);
$end_date = $this->input->post('end_date') != null ? $this->input->post('end_date') : $today;
$end_date_formatter = date_create_from_format($this->config->item('dateformat'), $end_date);
$is_valid_receipt = isset($search) ? $this->sale_lib->is_valid_receipt($search) : FALSE;
$sale_type = 'all';
$location_id = 'all';
$only_invoices = $this->input->post('only_invoices') != null;
$only_cash = $this->input->post('only_cash') != null;
$filters = array('sale_type' => $sale_type,
'location_id' => $location_id,
'start_date' => $start_date_formatter->format('Y-m-d'),
'end_date' => $end_date_formatter->format('Y-m-d'),
'only_invoices' => $only_invoices,
'only_cash' => $only_cash,
'is_valid_receipt' => $is_valid_receipt);
$sales = $this->Sale->search($search, $filters, $lines_per_page, $limit_from)->result_array();
$payments = $this->Sale->get_payments_summary($search, $filters);
$total_rows = $this->Sale->get_found_rows($search, $filters);
$links = $this->_initialize_pagination($this->Sale, $lines_per_page, $limit_from, $total_rows, 'search', $only_invoices);
$sale_rows = get_sales_manage_table_data_rows($sales, $this);
$payment_summary = get_sales_manage_payments_summary($payments, $sales, $this);
echo json_encode(array('total_rows' => $total_rows, 'rows' => $sale_rows, 'pagination' => $links, 'payment_summary' => $payment_summary));
}
function item_search()
{
$suggestions = array();
$search = $this->input->post('q');
$limit = $this->input->post('limit');
if ($this->sale_lib->get_mode() == 'return' && $this->sale_lib->is_valid_receipt($search) )
{
$suggestions[] = $search;
}
$suggestions = array_merge($suggestions, $this->Item->get_item_search_suggestions($search , $limit));
$suggestions = array_merge($suggestions, $this->Item_kit->get_item_kit_search_suggestions($search, $limit));
echo implode("\n", $suggestions);
}
function customer_search()
{
$search = $this->input->post('q');
$limit = $this->input->post('limit');
$suggestions = $this->Customer->get_customer_search_suggestions($search, $limit);
echo implode("\n", $suggestions);
}
function suggest()
{
$search = $this->input->post('q');
$limit = $this->input->post('limit');
$suggestions = $this->Sale->get_search_suggestions($search, $limit);
echo implode("\n", $suggestions);
}
function select_customer()
{
$customer_id = $this->input->post('customer');
if ($this->Customer->exists($customer_id))
{
$this->sale_lib->set_customer($customer_id);
}
$this->_reload();
}
function change_mode()
{
$stock_location = $this->input->post("stock_location");
if (!$stock_location || $stock_location == $this->sale_lib->get_sale_location())
{
$mode = $this->input->post("mode");
$this->sale_lib->set_mode($mode);
}
else if ($this->Stock_location->is_allowed_location($stock_location, 'sales'))
{
$this->sale_lib->set_sale_location($stock_location);
}
$this->_reload();
}
function set_comment()
{
$this->sale_lib->set_comment($this->input->post('comment'));
}
function set_invoice_number()
{
$this->sale_lib->set_invoice_number($this->input->post('sales_invoice_number'));
}
function set_invoice_number_enabled()
{
$this->sale_lib->set_invoice_number_enabled($this->input->post('sales_invoice_number_enabled'));
}
function set_print_after_sale()
{
$this->sale_lib->set_print_after_sale($this->input->post('sales_print_after_sale'));
}
function set_email_receipt()
{
$this->sale_lib->set_email_receipt($this->input->post('email_receipt'));
}
// Multiple Payments
function add_payment()
{
$data = array();
$this->form_validation->set_rules('amount_tendered', 'lang:sales_amount_tendered', 'trim|required|numeric');
if ( $this->form_validation->run() == FALSE )
{
if ( $this->input->post('payment_type') == $this->lang->line('sales_gift_card') )
{
$data['error']=$this->lang->line('sales_must_enter_numeric_giftcard');
}
else
{
$data['error']=$this->lang->line('sales_must_enter_numeric');
}
$this->_reload( $data );
return;
}
$payment_type = $this->input->post('payment_type');
if ( $payment_type == $this->lang->line('sales_giftcard') )
{
$payments = $this->sale_lib->get_payments();
$payment_type = $this->input->post('payment_type') . ':' . $payment_amount = $this->input->post('amount_tendered');
$current_payments_with_giftcard = isset($payments[$payment_type]) ? $payments[$payment_type]['payment_amount'] : 0;
$cur_giftcard_value = $this->Giftcard->get_giftcard_value($this->input->post('amount_tendered')) - $current_payments_with_giftcard;
if ( $cur_giftcard_value <= 0 )
{
$data['error'] = $this->lang->line('giftcards_remaining_balance', $this->input->post('amount_tendered'), to_currency( $this->Giftcard->get_giftcard_value( $this->input->post('amount_tendered'))));
$this->_reload( $data );
return;
}
$new_giftcard_value = $this->Giftcard->get_giftcard_value( $this->input->post('amount_tendered') ) - $this->sale_lib->get_amount_due();
$new_giftcard_value = ( $new_giftcard_value >= 0 ) ? $new_giftcard_value : 0;
$this->sale_lib->set_giftcard_remainder($new_giftcard_value);
$data['warning'] = $this->lang->line('giftcards_remaining_balance', $this->input->post('amount_tendered'), to_currency( $new_giftcard_value, TRUE ));
$payment_amount = min( $this->sale_lib->get_amount_due( ), $this->Giftcard->get_giftcard_value( $this->input->post('amount_tendered') ) );
}
else
{
$payment_amount = $this->input->post('amount_tendered');
}
if( !$this->sale_lib->add_payment( $payment_type, $payment_amount ) )
{
$data['error'] = 'Unable to Add Payment! Please try again!';
}
$this->_reload($data);
}
// Multiple Payments
function delete_payment( $payment_id )
{
$this->sale_lib->delete_payment( $payment_id );
$this->_reload();
}
function add()
{
$data = array();
$mode = $this->sale_lib->get_mode();
$item_id_or_number_or_item_kit_or_receipt = $this->input->post('item');
$quantity = ($mode == "return") ? -1 : 1;
$item_location = $this->sale_lib->get_sale_location();
if($mode == 'return' && $this->sale_lib->is_valid_receipt($item_id_or_number_or_item_kit_or_receipt))
{
$this->sale_lib->return_entire_sale($item_id_or_number_or_item_kit_or_receipt);
}
else if($this->sale_lib->is_valid_item_kit($item_id_or_number_or_item_kit_or_receipt))
{
$this->sale_lib->add_item_kit($item_id_or_number_or_item_kit_or_receipt,$item_location);
}
else if(!$this->sale_lib->add_item($item_id_or_number_or_item_kit_or_receipt,$quantity,$item_location,$this->config->item('default_sales_discount')))
{
$data['error'] = $this->lang->line('sales_unable_to_add_item');
}
$data['warning'] = $this->sale_lib->out_of_stock($item_id_or_number_or_item_kit_or_receipt,$item_location);
$this->_reload($data);
}
function edit_item($line)
{
$data = array();
$this->form_validation->set_rules('price', 'lang:items_price', 'required|numeric');
$this->form_validation->set_rules('quantity', 'lang:items_quantity', 'required|numeric');
$this->form_validation->set_rules('discount', 'lang:items_discount', 'required|numeric');
$description = $this->input->post('description');
$serialnumber = $this->input->post('serialnumber');
$price = $this->input->post('price');
$quantity = $this->input->post('quantity');
$discount = $this->input->post('discount');
$item_location = $this->input->post('location');
if ($this->form_validation->run() != FALSE)
{
$this->sale_lib->edit_item($line, $description, $serialnumber, $quantity, $discount, $price);
}
else
{
$data['error'] = $this->lang->line('sales_error_editing_item');
}
$data['warning'] = $this->sale_lib->out_of_stock($this->sale_lib->get_item_id($line),$item_location);
$this->_reload($data);
}
function delete_item($item_number)
{
$this->sale_lib->delete_item($item_number);
$this->_reload();
}
function remove_customer()
{
$this->sale_lib->clear_giftcard_remainder();
$this->sale_lib->clear_invoice_number();
$this->sale_lib->remove_customer();
$this->_reload();
}
function complete()
{
$data['cart'] = $this->sale_lib->get_cart();
$data['subtotal'] = $this->sale_lib->get_subtotal();
$data['discounted_subtotal'] = $this->sale_lib->get_subtotal(TRUE);
$data['tax_exclusive_subtotal'] = $this->sale_lib->get_subtotal(TRUE, TRUE);
$data['taxes'] = $this->sale_lib->get_taxes();
$data['total'] = $this->sale_lib->get_total();
$data['discount'] = $this->sale_lib->get_discount();
$data['receipt_title'] = $this->lang->line('sales_receipt');
$data['transaction_time'] = date($this->config->item('dateformat').' '.$this->config->item('timeformat'));
$data['transaction_date'] = date($this->config->item('dateformat'));
$data['show_stock_locations'] = $this->Stock_location->show_locations('sales');
$customer_id = $this->sale_lib->get_customer();
$employee_id = $this->Employee->get_logged_in_employee_info()->person_id;
$data['comments'] = $this->sale_lib->get_comment();
$emp_info=$this->Employee->get_info($employee_id);
$data['payments'] = $this->sale_lib->get_payments();
$data['amount_change'] = $this->sale_lib->get_amount_due() * -1;
$data['amount_due'] = $this->sale_lib->get_amount_due();
$data['employee'] = $emp_info->first_name.' '.$emp_info->last_name;
$data['company_info'] = implode("\n", array(
$this->config->item('address'),
$this->config->item('phone'),
$this->config->item('account_number')
));
$cust_info = '';
if($customer_id != -1)
{
$cust_info = $this->Customer->get_info($customer_id);
if (isset($cust_info->company_name))
{
$data['customer'] = $cust_info->company_name;
}
else
{
$data['customer'] = $cust_info->first_name.' '.$cust_info->last_name;
}
$data['customer_address'] = $cust_info->address_1;
$data['customer_location'] = $cust_info->zip . ' ' . $cust_info->city;
$data['account_number'] = $cust_info->account_number;
$data['customer_info'] = implode("\n", array(
$data['customer'],
$data['customer_address'],
$data['customer_location'],
$data['account_number']
));
}
$invoice_number = $this->_substitute_invoice_number($cust_info);
if ($this->sale_lib->is_invoice_number_enabled() && $this->Sale->invoice_number_exists($invoice_number))
{
$data['error'] = $this->lang->line('sales_invoice_number_duplicate');
$this->_reload($data);
}
else
{
$invoice_number = $this->sale_lib->is_invoice_number_enabled() ? $invoice_number : null;
$data['invoice_number'] = $invoice_number;
$data['sale_id'] = 'POS ' . $this->Sale->save($data['cart'], $customer_id, $employee_id, $data['comments'], $invoice_number, $data['payments']);
if ($data['sale_id'] == 'POS -1')
{
$data['error_message'] = $this->lang->line('sales_transaction_failed');
}
else
{
$data['barcode'] = $this->barcode_lib->generate_receipt_barcode($data['sale_id']);
// if we want to email. .. just attach the pdf in there?
if ($this->sale_lib->get_email_receipt() && !empty($cust_info->email))
{
$this->load->library('email');
$config['mailtype'] = 'html';
$this->email->initialize($config);
$this->email->from($this->config->item('email'), $this->config->item('company'));
$this->email->to($cust_info->email);
$this->email->subject($this->lang->line('sales_receipt'));
if ($this->config->item('use_invoice_template') && $this->sale_lib->is_invoice_number_enabled())
{
$data['image_prefix'] = "";
$filename = $this->_invoice_email_pdf($data);
$this->email->attach($filename);
$text = $this->config->item('invoice_email_message');
$text = str_replace('$INV', $invoice_number, $text);
$text = str_replace('$CO', $data['sale_id'], $text);
$text = $this->_substitute_customer($text, $cust_info);
$this->email->message($text);
}
else
{
$this->email->message($this->load->view("sales/receipt_email", $data, true));
}
$this->email->send();
}
}
$data['cur_giftcard_value'] = $this->sale_lib->get_giftcard_remainder();
$data['print_after_sale'] = $this->sale_lib->is_print_after_sale();
if ($this->sale_lib->is_invoice_number_enabled() && $this->config->item('use_invoice_template'))
{
$this->load->view("sales/invoice", $data);
}
else
{
$this->load->view("sales/receipt", $data);
}
$this->sale_lib->clear_all();
}
}
private function _invoice_email_pdf($data)
{
$data['image_prefix'] = "";
$html = $this->load->view('sales/invoice_email', $data, true);
// load pdf helper
$this->load->helper(array('dompdf', 'file'));
$file_content = pdf_create($html, '', false);
$filename = sys_get_temp_dir() . '/'. $this->lang->line('sales_invoice') .'-' . str_replace('/', '-' , $data["invoice_number"]) . '.pdf';
write_file($filename, $file_content);
return $filename;
}
function invoice_email($sale_id)
{
$sale_data = $this->_load_sale_data($sale_id);
$sale_data['image_prefix'] = base_url();
$this->load->view('sales/invoice_email', $sale_data);
$this->sale_lib->clear_all();
}
function send_invoice($sale_id)
{
$sale_data = $this->_load_sale_data($sale_id);
$text = $this->config->item('invoice_email_message');
$text = str_replace('$INV', $sale_data['invoice_number'], $text);
$text = str_replace('$CO', 'POS ' . $sale_data['sale_id'], $text);
$text = $this->_substitute_customer($text,(object) $sale_data);
$result = FALSE;
$message = $this->lang->line('sales_invoice_no_email');
if (isset($sale_data["customer_email"]) && !empty( $sale_data["customer_email"])) {
$this->load->library('email');
$this->email->from($this->config->item('email'), $this->config->item('company'));
$this->email->to($sale_data['customer_email']);
$this->email->subject($this->lang->line('sales_invoice') . ' ' . $sale_data['invoice_number']);
$this->email->message($text);
$filename = $this->_invoice_email_pdf($sale_data);
$this->email->attach($filename);
$result = $this->email->send();
$message = $this->lang->line($result ? 'sales_invoice_sent' : 'sales_invoice_unsent') . ' ' . $sale_data["customer_email"];
}
echo json_encode(array('success'=>$result, 'message'=>$message, 'id'=>$sale_id));
$this->sale_lib->clear_all();
}
private function _substitute_variable($text, $variable, $object, $function)
{
// don't query if this variable isn't used
if (strstr($text, $variable))
{
$value = call_user_func(array($object, $function));
$text = str_replace($variable, $value, $text);
}
return $text;
}
private function _substitute_customer($text, $cust_info)
{
// substitute customer info
$customer_id = $this->sale_lib->get_customer();
if($customer_id != -1 && $cust_info != '')
{
$text = str_replace('$CU',$cust_info->first_name . ' ' . $cust_info->last_name,$text);
$words = preg_split("/\s+/", trim($cust_info->first_name . ' ' . $cust_info->last_name));
$acronym = "";
foreach ($words as $w)
{
$acronym .= $w[0];
}
$text = str_replace('$CI',$acronym,$text);
}
return $text;
}
private function _is_custom_invoice_number($cust_info)
{
$invoice_number = $this->config->config['sales_invoice_format'];
$invoice_number = $this->_substitute_variables($invoice_number, $cust_info);
return $this->sale_lib->get_invoice_number() != $invoice_number;
}
private function _substitute_variables($text, $cust_info)
{
$text = $this->_substitute_variable($text, '$YCO', $this->Sale, 'get_invoice_number_for_year');
$text = $this->_substitute_variable($text, '$CO', $this->Sale , 'get_invoice_count');
$text = $this->_substitute_variable($text, '$SCO', $this->Sale_suspended, 'get_invoice_count');
$text = strftime($text);
$text = $this->_substitute_customer($text, $cust_info);
return $text;
}
private function _substitute_invoice_number($cust_info)
{
$invoice_number = $this->config->config['sales_invoice_format'];
$invoice_number = $this->_substitute_variables($invoice_number, $cust_info);
$this->sale_lib->set_invoice_number($invoice_number, TRUE);
return $this->sale_lib->get_invoice_number();
}
private function _load_sale_data($sale_id)
{
$this->Sale->create_sales_items_temp_table();
$this->sale_lib->clear_all();
$sale_info = $this->Sale->get_info($sale_id)->row_array();
$this->sale_lib->copy_entire_sale($sale_id);
$data['cart'] = $this->sale_lib->get_cart();
$data['payments'] = $this->sale_lib->get_payments();
$data['subtotal'] = $this->sale_lib->get_subtotal();
$data['discounted_subtotal'] = $this->sale_lib->get_subtotal(TRUE);
$data['tax_exclusive_subtotal'] = $this->sale_lib->get_subtotal(TRUE, TRUE);
$data['taxes'] = $this->sale_lib->get_taxes();
$data['total'] = $this->sale_lib->get_total();
$data['discount'] = $this->sale_lib->get_discount();
$data['receipt_title'] = $this->lang->line('sales_receipt');
$data['transaction_time'] = date($this->config->item('dateformat') . ' ' . $this->config->item('timeformat'), strtotime($sale_info['sale_time']));
$data['transaction_date'] = date($this->config->item('dateformat'), strtotime($sale_info['sale_time']));
$data['show_stock_locations'] = $this->Stock_location->show_locations('sales');
$customer_id = $this->sale_lib->get_customer();
$employee_id = $this->Employee->get_logged_in_employee_info()->person_id;
$emp_info = $this->Employee->get_info($employee_id);
$data['amount_change'] = $this->sale_lib->get_amount_due() * -1;
$data['amount_due'] = $this->sale_lib->get_amount_due();
$data['employee'] = $emp_info->first_name . ' ' . $emp_info->last_name;
if($customer_id != -1)
{
$cust_info = $this->Customer->get_info($customer_id);
if (isset($cust_info->company_name))
{
$data['customer'] = $cust_info->company_name;
}
else
{
$data['customer'] = $cust_info->first_name.' '.$cust_info->last_name;
}
$data['first_name'] = $cust_info->first_name;
$data['last_name'] = $cust_info->last_name;
$data['customer_address'] = $cust_info->address_1;
$data['customer_location'] = $cust_info->zip . ' ' . $cust_info->city;
$data['customer_email'] = $cust_info->email;
$data['account_number'] = $cust_info->account_number;
$data['customer_info'] = implode("\n", array(
$data['customer'],
$data['customer_address'],
$data['customer_location'],
$data['account_number']
));
}
$data['sale_id'] = 'POS '.$sale_id;
$data['comments'] = $sale_info['comment'];
$data['invoice_number'] = $sale_info['invoice_number'];
$data['company_info'] = implode("\n", array(
$this->config->item('address'),
$this->config->item('phone'),
$this->config->item('account_number')
));
$data['barcode'] = $this->barcode_lib->generate_receipt_barcode($data['sale_id']);
$data['print_after_sale'] = FALSE;
return $data;
}
function receipt($sale_id)
{
$data = $this->_load_sale_data($sale_id);
$this->load->view("sales/receipt", $data);
$this->sale_lib->clear_all();
}
function invoice($sale_id, $sale_info='')
{
if ($sale_info == '') {
$sale_info = $this->_load_sale_data($sale_id);
}
$this->load->view("sales/invoice",$sale_info);
$this->sale_lib->clear_all();
}
function edit($sale_id)
{
$data = array();
$data['employees'] = array();
foreach ($this->Employee->get_all()->result() as $employee)
{
$data['employees'][$employee->person_id] = $employee->first_name . ' '. $employee->last_name;
}
$this->Sale->create_sales_items_temp_table();
$sale_info = $this->Sale->get_info($sale_id)->row_array();
$person_name = $sale_info['first_name'] . " " . $sale_info['last_name'];
$data['selected_customer'] = !empty($sale_info['customer_id']) ? $sale_info['customer_id'] . "|" . $person_name : "";
$data['sale_info'] = $sale_info;
$this->load->view('sales/form', $data);
}
function delete($sale_id = -1, $update_inventory=TRUE)
{
$employee_id = $this->Employee->get_logged_in_employee_info()->person_id;
$sale_ids = $sale_id == -1 ? $this->input->post('ids') : array($sale_id);
if($this->Sale->delete_list($sale_ids, $employee_id, $update_inventory))
{
echo json_encode(array('success'=>true, 'message'=>$this->lang->line('sales_successfully_deleted').' '.
count($sale_ids).' '.$this->lang->line('sales_one_or_multiple'), 'ids'=>$sale_ids));
}
else
{
echo json_encode(array('success'=>false, 'message'=>$this->lang->line('sales_unsuccessfully_deleted')));
}
}
function save($sale_id)
{
$start_date_formatter = date_create_from_format($this->config->item('dateformat') . ' ' . $this->config->item('timeformat'), $this->input->post('date'));
$sale_data = array(
'sale_time' => $start_date_formatter->format('Y-m-d H:i:s'),
'customer_id' => $this->input->post('customer_id') != '' ? $this->input->post('customer_id') : null,
'employee_id' => $this->input->post('employee_id'),
'comment' => $this->input->post('comment'),
'invoice_number' => $this->input->post('invoice_number') != '' ? $this->input->post('invoice_number') : null
);
if ($this->Sale->update($sale_data, $sale_id))
{
echo json_encode(array('success'=>true, 'message'=>$this->lang->line('sales_successfully_updated'), 'id'=>$sale_id));
}
else
{
echo json_encode(array('success'=>false, 'message'=>$this->lang->line('sales_unsuccessfully_updated'), 'id'=>$sale_id));
}
}
private function _payments_cover_total()
{
$total_payments = 0;
foreach($this->sale_lib->get_payments() as $payment)
{
$total_payments += $payment['payment_amount'];
}
/* Changed the conditional to account for floating point rounding */
if ( ($this->sale_lib->get_mode() == 'sale') &&
( ( to_currency_no_money( $this->sale_lib->get_total() ) - $total_payments ) > 1e-6 ) )
{
return false;
}
return true;
}
private function _reload($data=array())
{
$person_info = $this->Employee->get_logged_in_employee_info();
$data['cart'] = $this->sale_lib->get_cart();
$data['modes'] = array('sale'=>$this->lang->line('sales_sale'), 'return'=>$this->lang->line('sales_return'));
$data['mode'] = $this->sale_lib->get_mode();
$data['stock_locations'] = $this->Stock_location->get_allowed_locations('sales');
$data['stock_location'] = $this->sale_lib->get_sale_location();
$data['subtotal'] = $this->sale_lib->get_subtotal(TRUE);
$data['tax_exclusive_subtotal'] = $this->sale_lib->get_subtotal(TRUE, TRUE);
$data['taxes'] = $this->sale_lib->get_taxes();
$data['discount'] = $this->sale_lib->get_discount();
$data['total'] = $this->sale_lib->get_total();
$data['items_module_allowed'] = $this->Employee->has_grant('items', $person_info->person_id);
$data['comment'] = $this->sale_lib->get_comment();
$data['email_receipt'] = $this->sale_lib->get_email_receipt();
$data['payments_total'] = $this->sale_lib->get_payments_total();
$data['amount_due'] = $this->sale_lib->get_amount_due();
$data['payments'] = $this->sale_lib->get_payments();
$data['payment_options'] = array(
$this->lang->line('sales_cash') => $this->lang->line('sales_cash'),
$this->lang->line('sales_check') => $this->lang->line('sales_check'),
$this->lang->line('sales_giftcard') => $this->lang->line('sales_giftcard'),
$this->lang->line('sales_debit') => $this->lang->line('sales_debit'),
$this->lang->line('sales_credit') => $this->lang->line('sales_credit')
);
$customer_id = $this->sale_lib->get_customer();
$cust_info = '';
if($customer_id != -1)
{
$cust_info = $this->Customer->get_info($customer_id);
$data['customer'] = $cust_info->first_name . ' ' . $cust_info->last_name;
$data['customer_email'] = $cust_info->email;
}
$data['invoice_number'] = $this->_substitute_invoice_number($cust_info);
$data['invoice_number_enabled'] = $this->sale_lib->is_invoice_number_enabled();
$data['print_after_sale'] = $this->sale_lib->is_print_after_sale();
$data['payments_cover_total'] = $this->_payments_cover_total();
$this->load->view("sales/register", $data);
}
function cancel_sale()
{
$this->sale_lib->clear_all();
$this->_reload();
}
function suspend()
{
$data['cart'] = $this->sale_lib->get_cart();
$data['subtotal'] = $this->sale_lib->get_subtotal();
$data['taxes'] = $this->sale_lib->get_taxes();
$data['total'] = $this->sale_lib->get_total();
$data['receipt_title'] = $this->lang->line('sales_receipt');
$data['transaction_time'] = date($this->config->item('dateformat') . ' ' . $this->config->item('timeformat'));
$customer_id = $this->sale_lib->get_customer();
$employee_id = $this->Employee->get_logged_in_employee_info()->person_id;
$comment = $this->sale_lib->get_comment();
$invoice_number = $this->sale_lib->get_invoice_number();
$emp_info = $this->Employee->get_info($employee_id);
$data['payment_type'] = $this->input->post('payment_type');
// Multiple payments
$data['payments'] = $this->sale_lib->get_payments();
$data['amount_change'] = to_currency($this->sale_lib->get_amount_due() * -1);
$data['employee'] = $emp_info->first_name.' '.$emp_info->last_name;
$cust_info = '';
if($customer_id != -1)
{
$cust_info = $this->Customer->get_info($customer_id);
if (isset($cust_info->company_name))
{
$data['customer'] = $cust_info->company_name;
}
else
{
$data['customer'] = $cust_info->first_name.' '.$cust_info->last_name;
}
}
$is_set = $this->_is_custom_invoice_number($cust_info);
$invoice_number = $is_set ? $invoice_number : NULL;
$total_payments = 0;
foreach($data['payments'] as $payment)
{
$total_payments = bcadd($total_payments, $payment['payment_amount'], PRECISION);
}
//SAVE sale to database
$data['sale_id'] = 'POS ' . $this->Sale_suspended->save($data['cart'], $customer_id, $employee_id, $comment, $invoice_number, $data['payments']);
if ($data['sale_id'] == 'POS -1')
{
$data['error_message'] = $this->lang->line('sales_transaction_failed');
}
$this->sale_lib->clear_all();
$this->_reload(array('success' => $this->lang->line('sales_successfully_suspended_sale')));
}
function suspended()
{
$data = array();
$data['suspended_sales'] = $this->Sale_suspended->get_all()->result_array();
$this->load->view('sales/suspended', $data);
}
function unsuspend()
{
$sale_id = $this->input->post('suspended_sale_id');
$this->sale_lib->clear_all();
$this->sale_lib->copy_entire_suspended_sale($sale_id);
$this->Sale_suspended->delete($sale_id);
$this->_reload();
}
function check_invoice_number()
{
$sale_id = $this->input->post('sale_id');
$invoice_number = $this->input->post('invoice_number');
$exists = !empty($invoice_number) && $this->Sale->invoice_number_exists($invoice_number,$sale_id);
echo !$exists ? 'true' : 'false';
}
}
?>

View File

@@ -0,0 +1,65 @@
<?php
class Secure_area extends CI_Controller
{
private $controller_name;
/*
Controllers that are considered secure extend Secure_area, optionally a $module_id can
be set to also check if a user can access a particular module in the system.
*/
function __construct($module_id=null,$submodule_id=null)
{
parent::__construct();
$this->load->model('Employee');
if(!$this->Employee->is_logged_in())
{
redirect('login');
}
$employee_id=$this->Employee->get_logged_in_employee_info()->person_id;
if(!$this->Employee->has_module_grant($module_id,$employee_id) ||
(isset($submodule_id) && !$this->Employee->has_module_grant($submodule_id,$employee_id)))
{
redirect('no_access/'.$module_id.'/'.$submodule_id);
}
//load up global data
$logged_in_employee_info=$this->Employee->get_logged_in_employee_info();
$data['allowed_modules']=$this->Module->get_allowed_modules($logged_in_employee_info->person_id);
$data['backup_allowed']=false;
foreach($data['allowed_modules']->result_array() as $module)
{
$data['backup_allowed']|=$module['module_id']==='config';
}
$data['user_info']=$logged_in_employee_info;
$data['controller_name']=$module_id;
$this->controller_name=$module_id;
$this->load->vars($data);
}
function get_controller_name()
{
return strtolower($this->controller_name);
}
function _initialize_pagination($object, $lines_per_page, $limit_from = 0, $total_rows = -1, $function='index', $filter='')
{
$this->load->library('pagination');
$config['base_url'] = site_url($this->get_controller_name() . "/$function/" . $filter);
$config['total_rows'] = $total_rows > -1 ? $total_rows : call_user_func(array($object, 'get_total_rows'));
$config['per_page'] = $lines_per_page;
$config['num_links'] = 2;
$config['last_link'] = $this->lang->line('common_last_page');
$config['first_link'] = $this->lang->line('common_first_page');
// page is calculated here instead of in pagination lib
$config['cur_page'] = $limit_from > 0 ? $limit_from : 0;
$config['page_query_string'] = FALSE;
$config['uri_segment'] = 0;
$this->pagination->initialize($config);
return $this->pagination->create_links();
}
}
?>

View File

@@ -1,5 +1,6 @@
<?php
require_once ("person_controller.php");
require_once ("Person_controller.php");
class Suppliers extends Person_controller
{
function __construct()
@@ -7,28 +8,33 @@ class Suppliers extends Person_controller
parent::__construct('suppliers');
}
function index()
function index($limit_from=0)
{
$config['base_url'] = site_url('/suppliers/index');
$config['total_rows'] = $this->Supplier->count_all();
$config['per_page'] = '20';
$config['uri_segment'] = 3;
$this->pagination->initialize($config);
$data['controller_name'] = $this->get_controller_name();
$data['form_width'] = $this->get_form_width();
$lines_per_page = $this->Appconfig->get('lines_per_page');
$suppliers = $this->Supplier->get_all($lines_per_page);
$data['controller_name']=strtolower(get_class());
$data['form_width']=$this->get_form_width();
$data['manage_table']=get_supplier_manage_table( $this->Supplier->get_all( $config['per_page'], $this->uri->segment( $config['uri_segment'] ) ), $this );
$this->load->view('suppliers/manage',$data);
$data['links'] = $this->_initialize_pagination($this->Supplier, $lines_per_page, $limit_from);
$data['manage_table'] = get_supplier_manage_table($suppliers, $this);
$this->load->view('suppliers/manage', $data);
}
/*
Returns supplier table data rows. This will be called with AJAX.
Returns Supplier table data rows. This will be called with AJAX.
*/
function search()
{
$search=$this->input->post('search');
$data_rows=get_supplier_manage_table_data_rows($this->Supplier->search($search),$this);
echo $data_rows;
$search = $this->input->post('search') != '' ? $this->input->post('search') : null;
$limit_from = $this->input->post('limit_from');
$lines_per_page = $this->Appconfig->get('lines_per_page');
$suppliers = $this->Supplier->search($search, $lines_per_page, $limit_from);
$total_rows = $this->Supplier->get_found_rows($search);
$links = $this->_initialize_pagination($this->Supplier, $lines_per_page, $limit_from, $total_rows);
$data_rows = get_supplier_manage_table_data_rows($suppliers, $this);
echo json_encode(array('total_rows' => $total_rows, 'rows' => $data_rows, 'pagination' => $links));
}
/*
@@ -55,23 +61,25 @@ class Suppliers extends Person_controller
function save($supplier_id=-1)
{
$person_data = array(
'first_name'=>$this->input->post('first_name'),
'last_name'=>$this->input->post('last_name'),
'email'=>$this->input->post('email'),
'phone_number'=>$this->input->post('phone_number'),
'address_1'=>$this->input->post('address_1'),
'address_2'=>$this->input->post('address_2'),
'city'=>$this->input->post('city'),
'state'=>$this->input->post('state'),
'zip'=>$this->input->post('zip'),
'country'=>$this->input->post('country'),
'comments'=>$this->input->post('comments')
'first_name'=>$this->input->post('first_name'),
'last_name'=>$this->input->post('last_name'),
'gender'=>$this->input->post('gender'),
'email'=>$this->input->post('email'),
'phone_number'=>$this->input->post('phone_number'),
'address_1'=>$this->input->post('address_1'),
'address_2'=>$this->input->post('address_2'),
'city'=>$this->input->post('city'),
'state'=>$this->input->post('state'),
'zip'=>$this->input->post('zip'),
'country'=>$this->input->post('country'),
'comments'=>$this->input->post('comments')
);
$supplier_data=array(
'company_name'=>$this->input->post('company_name'),
'account_number'=>$this->input->post('account_number')=='' ? null:$this->input->post('account_number'),
'company_name'=>$this->input->post('company_name'),
'agency_name'=>$this->input->post('agency_name'),
'account_number'=>$this->input->post('account_number') == '' ? null : $this->input->post('account_number')
);
if($this->Supplier->save($person_data,$supplier_data,$supplier_id))
if($this->Supplier->save_supplier($person_data,$supplier_data,$supplier_id))
{
//New supplier
if($supplier_id==-1)
@@ -127,5 +135,5 @@ class Suppliers extends Person_controller
{
return 360;
}
}
}
?>

View File

@@ -1,77 +0,0 @@
<?php
require_once ("secure_area.php");
class Config extends Secure_area
{
function __construct()
{
parent::__construct('config');
}
function index()
{
$location_names = array();
$locations = $this->Stock_locations->get_location_names();
foreach($locations->result_array() as $array)
{
array_push($location_names, $array['location_name']);
}
$data['location_names'] = implode(',', $location_names);
$this->load->view("config", $data);
}
function save()
{
$batch_save_data=array(
'company'=>$this->input->post('company'),
'address'=>$this->input->post('address'),
'phone'=>$this->input->post('phone'),
'email'=>$this->input->post('email'),
'fax'=>$this->input->post('fax'),
'website'=>$this->input->post('website'),
'default_tax_1_rate'=>$this->input->post('default_tax_1_rate'),
'default_tax_1_name'=>$this->input->post('default_tax_1_name'),
'default_tax_2_rate'=>$this->input->post('default_tax_2_rate'),
'default_tax_2_name'=>$this->input->post('default_tax_2_name'),
'currency_symbol'=>$this->input->post('currency_symbol'),
'currency_side'=>$this->input->post('currency_side'),/**GARRISON ADDED 4/20/2013**/
'return_policy'=>$this->input->post('return_policy'),
'language'=>$this->input->post('language'),
'timezone'=>$this->input->post('timezone'),
'print_after_sale'=>$this->input->post('print_after_sale'),
'custom1_name'=>$this->input->post('custom1_name'),/**GARRISON ADDED 4/20/2013**/
'custom2_name'=>$this->input->post('custom2_name'),/**GARRISON ADDED 4/20/2013**/
'custom3_name'=>$this->input->post('custom3_name'),/**GARRISON ADDED 4/20/2013**/
'custom4_name'=>$this->input->post('custom4_name'),/**GARRISON ADDED 4/20/2013**/
'custom5_name'=>$this->input->post('custom5_name'),/**GARRISON ADDED 4/20/2013**/
'custom6_name'=>$this->input->post('custom6_name'),/**GARRISON ADDED 4/20/2013**/
'custom7_name'=>$this->input->post('custom7_name'),/**GARRISON ADDED 4/20/2013**/
'custom8_name'=>$this->input->post('custom8_name'),/**GARRISON ADDED 4/20/2013**/
'custom9_name'=>$this->input->post('custom9_name'),/**GARRISON ADDED 4/20/2013**/
'custom10_name'=>$this->input->post('custom10_name')/**GARRISON ADDED 4/20/2013**/
);
$stock_locations = explode( ',', $this->input->post('stock_location'));
$stock_locations_trimmed=array();
foreach($stock_locations as $location)
{
array_push($stock_locations_trimmed, trim($location, ' '));
}
$current_locations = $this->Stock_locations->concat_location_names()->location_names;
if ($this->input->post('stock_locations') != $current_locations)
{
$this->load->library('sale_lib');
$this->sale_lib->clear_sale_location();
$this->sale_lib->clear_all();
$this->load->library('receiving_lib');
$this->receiving_lib->clear_stock_source();
$this->receiving_lib->clear_stock_destination();
$this->receiving_lib->clear_all();
}
if( $this->Appconfig->batch_save( $batch_save_data ) && $this->Stock_locations->array_save($stock_locations_trimmed))
{
echo json_encode(array('success'=>true,'message'=>$this->lang->line('config_saved_successfully')));
}
}
}
?>

View File

@@ -1,117 +0,0 @@
<?php
require_once ("secure_area.php");
require_once ("interfaces/idata_controller.php");
class Giftcards extends Secure_area implements iData_controller
{
function __construct()
{
parent::__construct('giftcards');
}
function index()
{
$config['base_url'] = site_url('/giftcards/index');
$config['total_rows'] = $this->Giftcard->count_all();
$config['per_page'] = '20';
$config['uri_segment'] = 3;
$this->pagination->initialize($config);
$data['controller_name']=strtolower(get_class());
$data['form_width']=$this->get_form_width();
$data['manage_table']=get_giftcards_manage_table( $this->Giftcard->get_all( $config['per_page'], $this->uri->segment( $config['uri_segment'] ) ), $this );
$this->load->view('giftcards/manage',$data);
}
function search()
{
$search=$this->input->post('search');
$data_rows=get_giftcards_manage_table_data_rows($this->Giftcard->search($search),$this);
echo $data_rows;
}
/*
Gives search suggestions based on what is being searched for
*/
function suggest()
{
$suggestions = $this->Giftcard->get_search_suggestions($this->input->post('q'),$this->input->post('limit'));
echo implode("\n",$suggestions);
}
/** GARRISON ADDED 5/3/2013 **/
/*
Gives search suggestions for person_id based on what is being searched for
*/
function suggest_person()
{
$suggestions = $this->Giftcard->get_person_search_suggestions($this->input->post('q'),$this->input->post('limit'));
echo implode("\n",$suggestions);
}
/** END GARRISON ADDED **/
function get_row()
{
$giftcard_id = $this->input->post('row_id');
$data_row=get_giftcard_data_row($this->Giftcard->get_info($giftcard_id),$this);
echo $data_row;
}
function view($giftcard_id=-1)
{
$data['giftcard_info']=$this->Giftcard->get_info($giftcard_id);
$this->load->view("giftcards/form",$data);
}
function save($giftcard_id=-1)
{
$giftcard_data = array(
'giftcard_number'=>$this->input->post('giftcard_number'),
'value'=>$this->input->post('value'),
'person_id'=>$this->input->post('person_id')/**GARRISON ADDED 4/22/2013**/
);
if( $this->Giftcard->save( $giftcard_data, $giftcard_id ) )
{
//New giftcard
if($giftcard_id==-1)
{
echo json_encode(array('success'=>true,'message'=>$this->lang->line('giftcards_successful_adding').' '.
$giftcard_data['giftcard_number'],'giftcard_id'=>$giftcard_data['giftcard_id']));
$giftcard_id = $giftcard_data['giftcard_id'];
}
else //previous giftcard
{
echo json_encode(array('success'=>true,'message'=>$this->lang->line('giftcards_successful_updating').' '.
$giftcard_data['giftcard_number'],'giftcard_id'=>$giftcard_id));
}
}
else//failure
{
echo json_encode(array('success'=>false,'message'=>$this->lang->line('giftcards_error_adding_updating').' '.
$giftcard_data['giftcard_number'],'giftcard_id'=>-1));
}
}
function delete()
{
$giftcards_to_delete=$this->input->post('ids');
if($this->Giftcard->delete_list($giftcards_to_delete))
{
echo json_encode(array('success'=>true,'message'=>$this->lang->line('giftcards_successful_deleted').' '.
count($giftcards_to_delete).' '.$this->lang->line('giftcards_one_or_multiple')));
}
else
{
echo json_encode(array('success'=>false,'message'=>$this->lang->line('giftcards_cannot_be_deleted')));
}
}
/*
get the width for the add/edit form
*/
function get_form_width()
{
return 360;
}
}
?>

View File

@@ -1,3 +1,4 @@
<!DOCTYPE html>
<html>
<head>
<title>403 Forbidden</title>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
</html>
</html>

View File

@@ -3,7 +3,7 @@
This interface is implemented by any controller that keeps track of people, such
as customers and employees.
*/
require_once("idata_controller.php");
require_once("Idata_controller.php");
interface iPerson_controller extends iData_controller
{
public function mailto();

View File

@@ -1,138 +0,0 @@
<?php
require_once ("secure_area.php");
require_once ("interfaces/idata_controller.php");
class Item_kits extends Secure_area implements iData_controller
{
function __construct()
{
parent::__construct('item_kits');
}
function index()
{
$config['base_url'] = site_url('/item_kits/index');
$config['total_rows'] = $this->Item_kit->count_all();
$config['per_page'] = '20';
$config['uri_segment'] = 3;
$this->pagination->initialize($config);
$data['controller_name']=strtolower(get_class());
$data['form_width']=$this->get_form_width();
$data['manage_table']=get_item_kits_manage_table( $this->Item_kit->get_all( $config['per_page'], $this->uri->segment( $config['uri_segment'] ) ), $this );
$this->load->view('item_kits/manage',$data);
}
function search()
{
$search=$this->input->post('search');
$data_rows=get_item_kits_manage_table_data_rows($this->Item_kit->search($search),$this);
echo $data_rows;
}
/*
Gives search suggestions based on what is being searched for
*/
function suggest()
{
$suggestions = $this->Item_kit->get_search_suggestions($this->input->post('q'),$this->input->post('limit'));
echo implode("\n",$suggestions);
}
function get_row()
{
$item_kit_id = $this->input->post('row_id');
$data_row=get_item_kit_data_row($this->Item_kit->get_info($item_kit_id),$this);
echo $data_row;
}
function view($item_kit_id=-1)
{
$data['item_kit_info']=$this->Item_kit->get_info($item_kit_id);
$this->load->view("item_kits/form",$data);
}
function save($item_kit_id=-1)
{
$item_kit_data = array(
'name'=>$this->input->post('name'),
'description'=>$this->input->post('description')
);
if($this->Item_kit->save($item_kit_data,$item_kit_id))
{
//New item kit
if($item_kit_id==-1)
{
echo json_encode(array('success'=>true,'message'=>$this->lang->line('item_kits_successful_adding').' '.
$item_kit_data['name'],'item_kit_id'=>$item_kit_data['item_kit_id']));
$item_kit_id = $item_kit_data['item_kit_id'];
}
else //previous item
{
echo json_encode(array('success'=>true,'message'=>$this->lang->line('item_kits_successful_updating').' '.
$item_kit_data['name'],'item_kit_id'=>$item_kit_id));
}
if ($this->input->post('item_kit_item'))
{
$item_kit_items = array();
foreach($this->input->post('item_kit_item') as $item_id => $quantity)
{
$item_kit_items[] = array(
'item_id' => $item_id,
'quantity' => $quantity
);
}
$this->Item_kit_items->save($item_kit_items, $item_kit_id);
}
}
else//failure
{
echo json_encode(array('success'=>false,'message'=>$this->lang->line('item_kits_error_adding_updating').' '.
$item_kit_data['name'],'item_kit_id'=>-1));
}
}
function delete()
{
$item_kits_to_delete=$this->input->post('ids');
if($this->Item_kit->delete_list($item_kits_to_delete))
{
echo json_encode(array('success'=>true,'message'=>$this->lang->line('item_kits_successful_deleted').' '.
count($item_kits_to_delete).' '.$this->lang->line('item_kits_one_or_multiple')));
}
else
{
echo json_encode(array('success'=>false,'message'=>$this->lang->line('item_kits_cannot_be_deleted')));
}
}
function generate_barcodes($item_kit_ids)
{
$result = array();
$item_kit_ids = explode(':', $item_kit_ids);
foreach ($item_kit_ids as $item_kid_id)
{
$item_kit_info = $this->Item_kit->get_info($item_kid_id);
$result[] = array('name' =>$item_kit_info->name, 'id'=> 'KIT '.$item_kid_id);
}
$data['items'] = $result;
$this->load->view("barcode_sheet", $data);
}
/*
get the width for the add/edit form
*/
function get_form_width()
{
return 360;
}
}
?>

View File

@@ -1,611 +0,0 @@
<?php
require_once ("secure_area.php");
require_once ("interfaces/idata_controller.php");
class Items extends Secure_area implements iData_controller
{
function __construct()
{
parent::__construct('items');
}
function index()
{
$config['base_url'] = site_url('/items/index');
$config['total_rows'] = $this->Item->count_all();
$config['per_page'] = '20';
$config['uri_segment'] = 3;
$this->pagination->initialize($config);
$data['controller_name']=strtolower(get_class());
$data['form_width']=$this->get_form_width();
$data['manage_table']=get_items_manage_table( $this->Item->get_all( $config['per_page'], $this->uri->segment( $config['uri_segment'] ) ), $this );
$this->load->view('items/manage',$data);
}
function refresh()
{
$is_serialized=$this->input->post('is_serialized');
$no_description=$this->input->post('no_description');
$search_custom=$this->input->post('search_custom');//GARRISON ADDED 4/13/2013
$is_deleted=$this->input->post('is_deleted'); // Parq 131215
$data['search_section_state']=$this->input->post('search_section_state');
$data['is_serialized']=$this->input->post('is_serialized');
$data['no_description']=$this->input->post('no_description');
$data['search_custom']=$this->input->post('search_custom');//GARRISON ADDED 4/13/2013
$data['is_deleted']=$this->input->post('is_deleted'); // Parq 131215
$data['controller_name']=strtolower(get_class());
$data['form_width']=$this->get_form_width();
$data['manage_table']=get_items_manage_table($this->Item->get_all_filtered($is_serialized,$no_description,$search_custom,$is_deleted),$this);//GARRISON MODIFIED 4/13/2013, Parq 131215
$this->load->view('items/manage',$data);
}
function find_item_info()
{
$item_number=$this->input->post('scan_item_number');
echo json_encode($this->Item->find_item_info($item_number));
}
function search()
{
$search=$this->input->post('search');
$data_rows=get_items_manage_table_data_rows($this->Item->search($search),$this);
echo $data_rows;
}
/*
Gives search suggestions based on what is being searched for
*/
function suggest()
{
$suggestions = $this->Item->get_search_suggestions($this->input->post('q'),$this->input->post('limit'));
echo implode("\n",$suggestions);
}
function item_search()
{
$suggestions = $this->Item->get_item_search_suggestions($this->input->post('q'),$this->input->post('limit'),'warehouse');
echo implode("\n",$suggestions);
}
/*
Gives search suggestions based on what is being searched for
*/
function suggest_category()
{
$suggestions = $this->Item->get_category_suggestions($this->input->post('q'));
echo implode("\n",$suggestions);
}
/**GARRISON ADDED 5/18/2013**/
/*
Gives search suggestions based on what is being searched for
*/
function suggest_location()
{
$suggestions = $this->Item->get_location_suggestions($this->input->post('q'));
echo implode("\n",$suggestions);
}
/*
Gives search suggestions based on what is being searched for
*/
function suggest_custom1()
{
$suggestions = $this->Item->get_custom1_suggestions($this->input->post('q'));
echo implode("\n",$suggestions);
}
/*
Gives search suggestions based on what is being searched for
*/
function suggest_custom2()
{
$suggestions = $this->Item->get_custom2_suggestions($this->input->post('q'));
echo implode("\n",$suggestions);
}
/*
Gives search suggestions based on what is being searched for
*/
function suggest_custom3()
{
$suggestions = $this->Item->get_custom3_suggestions($this->input->post('q'));
echo implode("\n",$suggestions);
}
/*
Gives search suggestions based on what is being searched for
*/
function suggest_custom4()
{
$suggestions = $this->Item->get_custom4_suggestions($this->input->post('q'));
echo implode("\n",$suggestions);
}
/*
Gives search suggestions based on what is being searched for
*/
function suggest_custom5()
{
$suggestions = $this->Item->get_custom5_suggestions($this->input->post('q'));
echo implode("\n",$suggestions);
}
/*
Gives search suggestions based on what is being searched for
*/
function suggest_custom6()
{
$suggestions = $this->Item->get_custom6_suggestions($this->input->post('q'));
echo implode("\n",$suggestions);
}
/*
Gives search suggestions based on what is being searched for
*/
function suggest_custom7()
{
$suggestions = $this->Item->get_custom7_suggestions($this->input->post('q'));
echo implode("\n",$suggestions);
}
/*
Gives search suggestions based on what is being searched for
*/
function suggest_custom8()
{
$suggestions = $this->Item->get_custom8_suggestions($this->input->post('q'));
echo implode("\n",$suggestions);
}
/*
Gives search suggestions based on what is being searched for
*/
function suggest_custom9()
{
$suggestions = $this->Item->get_custom9_suggestions($this->input->post('q'));
echo implode("\n",$suggestions);
}
/*
Gives search suggestions based on what is being searched for
*/
function suggest_custom10()
{
$suggestions = $this->Item->get_custom10_suggestions($this->input->post('q'));
echo implode("\n",$suggestions);
}
/**END GARRISON ADDED**/
function get_row()
{
$item_id = $this->input->post('row_id');
$data_row=get_item_data_row($this->Item->get_info($item_id),$this);
echo $data_row;
}
function view($item_id=-1)
{
$data['item_info']=$this->Item->get_info($item_id);
$data['item_tax_info']=$this->Item_taxes->get_info($item_id);
$suppliers = array('' => $this->lang->line('items_none'));
foreach($this->Supplier->get_all()->result_array() as $row)
{
$suppliers[$row['person_id']] = $row['company_name'] .' ('.$row['first_name'] .' '. $row['last_name'].')';
}
$data['suppliers']=$suppliers;
$data['selected_supplier'] = $this->Item->get_info($item_id)->supplier_id;
$data['default_tax_1_rate']=($item_id==-1) ? $this->Appconfig->get('default_tax_1_rate') : '';
$data['default_tax_2_rate']=($item_id==-1) ? $this->Appconfig->get('default_tax_2_rate') : '';
$locations_data = $this->Stock_locations->get_undeleted_all()->result_array();
$location_array;
foreach($locations_data as $location)
{
$quantity = $this->Item_quantities->get_item_quantity($item_id, $location['location_id'])->quantity;
$quantity = ($item_id == -1) ? null: $quantity;
$location_array[$location['location_id']] = array('location_name'=>$location['location_name'],
'quantity'=>$quantity);
}
$data['stock_locations']= $location_array;
$this->load->view("items/form",$data);
}
//Ramel Inventory Tracking
function inventory($item_id=-1)
{
$data['item_info']=$this->Item->get_info($item_id);
$data['stock_locations'] = array();
$stock_locations = $this->Stock_locations->get_undeleted_all()->result_array();
foreach($stock_locations as $location_data)
{
$data['stock_locations'][$location_data['location_id']] = $location_data['location_name'];
$data['item_quantities'][$location_data['location_id']] = $this->Item_quantities->get_item_quantity($item_id,$location_data['location_id'])->quantity;
}
$this->load->view("items/inventory",$data);
}
function count_details($item_id=-1)
{
$data['item_info']=$this->Item->get_info($item_id);
$data['stock_locations'] = array();
$stock_locations = $this->Stock_locations->get_undeleted_all()->result_array();
foreach($stock_locations as $location_data)
{
$data['stock_locations'][$location_data['location_id']] = $location_data['location_name'];
$data['item_quantities'][$location_data['location_id']] = $this->Item_quantities->get_item_quantity($item_id,$location_data['location_id'])->quantity;
}
$this->load->view("items/count_details",$data);
} //------------------------------------------- Ramel
function generate_barcodes($item_ids)
{
$result = array();
$item_ids = explode(':', $item_ids);
foreach ($item_ids as $item_id)
{
$item_info = $this->Item->get_info($item_id);
$result[] = array('name' =>$item_info->name, 'id'=> $item_id);
}
$data['items'] = $result;
$this->load->view("barcode_sheet", $data);
}
function bulk_edit()
{
$data = array();
$suppliers = array('' => $this->lang->line('items_none'));
foreach($this->Supplier->get_all()->result_array() as $row)
{
$suppliers[$row['person_id']] = $row['first_name'] .' '. $row['last_name'];
}
$data['suppliers'] = $suppliers;
$data['allow_alt_description_choices'] = array(
''=>$this->lang->line('items_do_nothing'),
1 =>$this->lang->line('items_change_all_to_allow_alt_desc'),
0 =>$this->lang->line('items_change_all_to_not_allow_allow_desc'));
$data['serialization_choices'] = array(
''=>$this->lang->line('items_do_nothing'),
1 =>$this->lang->line('items_change_all_to_serialized'),
0 =>$this->lang->line('items_change_all_to_unserialized'));
$this->load->view("items/form_bulk", $data);
}
function save($item_id=-1)
{
//Save item data
$item_data = array(
'name'=>$this->input->post('name'),
'description'=>$this->input->post('description'),
'category'=>$this->input->post('category'),
'supplier_id'=>$this->input->post('supplier_id')=='' ? null:$this->input->post('supplier_id'),
'item_number'=>$this->input->post('item_number')=='' ? null:$this->input->post('item_number'),
'cost_price'=>$this->input->post('cost_price'),
'unit_price'=>$this->input->post('unit_price'),
'reorder_level'=>$this->input->post('reorder_level'),
'allow_alt_description'=>$this->input->post('allow_alt_description'),
'is_serialized'=>$this->input->post('is_serialized'),
'deleted'=>$this->input->post('is_deleted'), /** Parq 131215 **/
'custom1'=>$this->input->post('custom1'), /**GARRISON ADDED 4/21/2013**/
'custom2'=>$this->input->post('custom2'),/**GARRISON ADDED 4/21/2013**/
'custom3'=>$this->input->post('custom3'),/**GARRISON ADDED 4/21/2013**/
'custom4'=>$this->input->post('custom4'),/**GARRISON ADDED 4/21/2013**/
'custom5'=>$this->input->post('custom5'),/**GARRISON ADDED 4/21/2013**/
'custom6'=>$this->input->post('custom6'),/**GARRISON ADDED 4/21/2013**/
'custom7'=>$this->input->post('custom7'),/**GARRISON ADDED 4/21/2013**/
'custom8'=>$this->input->post('custom8'),/**GARRISON ADDED 4/21/2013**/
'custom9'=>$this->input->post('custom9'),/**GARRISON ADDED 4/21/2013**/
'custom10'=>$this->input->post('custom10')/**GARRISON ADDED 4/21/2013**/
);
$employee_id=$this->Employee->get_logged_in_employee_info()->person_id;
$cur_item_info = $this->Item->get_info($item_id);
if($this->Item->save($item_data,$item_id))
{
//New item
if($item_id==-1)
{
echo json_encode(array('success'=>true,'message'=>$this->lang->line('items_successful_adding').' '.
$item_data['name'],'item_id'=>$item_data['item_id']));
$item_id = $item_data['item_id'];
}
else //previous item
{
echo json_encode(array('success'=>true,'message'=>$this->lang->line('items_successful_updating').' '.
$item_data['name'],'item_id'=>$item_id));
}
$items_taxes_data = array();
$tax_names = $this->input->post('tax_names');
$tax_percents = $this->input->post('tax_percents');
for($k=0;$k<count($tax_percents);$k++)
{
if (is_numeric($tax_percents[$k]))
{
$items_taxes_data[] = array('name'=>$tax_names[$k], 'percent'=>$tax_percents[$k] );
}
}
$this->Item_taxes->save($items_taxes_data, $item_id);
//Save item quantity
$stock_locations = $this->Stock_locations->get_undeleted_all()->result_array();
foreach($stock_locations as $location_data)
{
$updated_quantity = $this->input->post($location_data['location_id'].'_quantity');
$location_detail = array('item_id'=>$item_id,
'location_id'=>$location_data['location_id'],
'quantity'=>$updated_quantity);
$item_quantity = $this->Item_quantities->get_item_quantity($item_id, $location_data['location_id']);
if ($item_quantity->quantity != $updated_quantity)
{
$this->Item_quantities->save($location_detail, $item_id, $location_data['location_id']);
$inv_data = array
(
'trans_date'=>date('Y-m-d H:i:s'),
'trans_items'=>$item_id,
'trans_user'=>$employee_id,
'trans_location'=>$location_data['location_id'],
'trans_comment'=>$this->lang->line('items_manually_editing_of_quantity'),
'trans_inventory'=>$updated_quantity - $item_quantity->quantity
);
$this->Inventory->insert($inv_data);
}
}
}
else//failure
{
echo json_encode(array('success'=>false,'message'=>$this->lang->line('items_error_adding_updating').' '.
$item_data['name'],'item_id'=>-1));
}
}
//Ramel Inventory Tracking
function save_inventory($item_id=-1)
{
$employee_id=$this->Employee->get_logged_in_employee_info()->person_id;
$cur_item_info = $this->Item->get_info($item_id);
$location_id = $this->input->post('stock_location');
$inv_data = array
(
'trans_date'=>date('Y-m-d H:i:s'),
'trans_items'=>$item_id,
'trans_user'=>$employee_id,
'trans_location'=>$location_id,
'trans_comment'=>$this->input->post('trans_comment'),
'trans_inventory'=>$this->input->post('newquantity')
);
$this->Inventory->insert($inv_data);
//Update stock quantity
$item_quantity= $this->Item_quantities->get_item_quantity($item_id,$location_id);
$item_quantity_data = array(
'item_id'=>$item_id,
'location_id'=>$location_id,
'quantity'=>$item_quantity->quantity + $this->input->post('newquantity')
);
if($this->Item_quantities->save($item_quantity_data,$item_id,$location_id))
{
echo json_encode(array('success'=>true,'message'=>$this->lang->line('items_successful_updating').' '.
$cur_item_info->name,'item_id'=>$item_id));
}
else//failure
{
echo json_encode(array('success'=>false,'message'=>$this->lang->line('items_error_adding_updating').' '.
$cur_item_info->name,'item_id'=>-1));
}
}//---------------------------------------------------------------------Ramel
function bulk_update()
{
$items_to_update=$this->input->post('item_ids');
$item_data = array();
foreach($_POST as $key=>$value)
{
//This field is nullable, so treat it differently
if ($key == 'supplier_id')
{
$item_data["$key"]=$value == '' ? null : $value;
}
elseif($value!='' and !(in_array($key, array('item_ids', 'tax_names', 'tax_percents'))))
{
$item_data["$key"]=$value;
}
}
//Item data could be empty if tax information is being updated
if(empty($item_data) || $this->Item->update_multiple($item_data,$items_to_update))
{
$items_taxes_data = array();
$tax_names = $this->input->post('tax_names');
$tax_percents = $this->input->post('tax_percents');
for($k=0;$k<count($tax_percents);$k++)
{
if (is_numeric($tax_percents[$k]))
{
$items_taxes_data[] = array('name'=>$tax_names[$k], 'percent'=>$tax_percents[$k] );
}
}
$this->Item_taxes->save_multiple($items_taxes_data, $items_to_update);
echo json_encode(array('success'=>true,'message'=>$this->lang->line('items_successful_bulk_edit')));
}
else
{
echo json_encode(array('success'=>false,'message'=>$this->lang->line('items_error_updating_multiple')));
}
}
function delete()
{
$items_to_delete=$this->input->post('ids');
if($this->Item->delete_list($items_to_delete))
{
echo json_encode(array('success'=>true,'message'=>$this->lang->line('items_successful_deleted').' '.
count($items_to_delete).' '.$this->lang->line('items_one_or_multiple')));
}
else
{
echo json_encode(array('success'=>false,'message'=>$this->lang->line('items_cannot_be_deleted')));
}
}
function excel()
{
$data = file_get_contents("import_items.csv");
$name = 'import_items.csv';
force_download($name, $data);
}
function excel_import()
{
$this->load->view("items/excel_import", null);
}
function do_excel_import()
{
$msg = 'do_excel_import';
$failCodes = array();
if ($_FILES['file_path']['error']!=UPLOAD_ERR_OK)
{
$msg = $this->lang->line('items_excel_import_failed');
echo json_encode( array('success'=>false,'message'=>$msg) );
return;
}
else
{
if (($handle = fopen($_FILES['file_path']['tmp_name'], "r")) !== FALSE)
{
//Skip first row
fgetcsv($handle);
$i=1;
while (($data = fgetcsv($handle)) !== FALSE)
{
$item_data = array(
'name' => $data[1],
'description' => $data[13],
'category' => $data[2],
'cost_price' => $data[4],
'unit_price' => $data[5],
'quantity' => $data[10],
'reorder_level' => $data[11],
'supplier_id' => $this->Supplier->exists($data[3]) ? $data[3] : null,
'allow_alt_description' => $data[14] != '' ? '1' : '0',
'is_serialized' => $data[15] != '' ? '1' : '0',
'custom1' => $data[16], /** GARRISON ADDED 5/6/2013 **/
'custom2' => $data[17], /** GARRISON ADDED 5/6/2013 **/
'custom3' => $data[18], /** GARRISON ADDED 5/6/2013 **/
'custom4' => $data[19], /** GARRISON ADDED 5/6/2013 **/
'custom5' => $data[20], /** GARRISON ADDED 5/6/2013 **/
'custom6' => $data[21], /** GARRISON ADDED 5/6/2013 **/
'custom7' => $data[22], /** GARRISON ADDED 5/6/2013 **/
'custom8' => $data[23], /** GARRISON ADDED 5/6/2013 **/
'custom9' => $data[24], /** GARRISON ADDED 5/6/2013 **/
'custom10' => $data[25] /** GARRISON ADDED 5/6/2013 **/
);
$item_number = $data[0];
if ($item_number != "")
{
$item_data['item_number'] = $item_number;
}
if($this->Item->save($item_data))
{
$items_taxes_data = null;
//tax 1
if( is_numeric($data[7]) && $data[6]!='' )
{
$items_taxes_data[] = array('name'=>$data[6], 'percent'=>$data[7] );
}
//tax 2
if( is_numeric($data[9]) && $data[8]!='' )
{
$items_taxes_data[] = array('name'=>$data[8], 'percent'=>$data[9] );
}
// save tax values
if(count($items_taxes_data) > 0)
{
$this->Item_taxes->save($items_taxes_data, $item_data['item_id']);
}
$employee_id=$this->Employee->get_logged_in_employee_info()->person_id;
$emp_info=$this->Employee->get_info($employee_id);
$comment ='Qty CSV Imported';
$excel_data = array
(
'trans_items'=>$item_data['item_id'],
'trans_user'=>$employee_id,
'trans_comment'=>$comment,
'trans_inventory'=>$data[10]
);
$this->db->insert('inventory',$excel_data);
//------------------------------------------------Ramel
}
else//insert or update item failure
{
$failCodes[] = $i;
}
}
$i++;
}
else
{
echo json_encode( array('success'=>false,'message'=>'Your upload file has no data or not in supported format.') );
return;
}
}
$success = true;
if(count($failCodes) > 1)
{
$msg = "Most items imported. But some were not, here is list of their CODE (" .count($failCodes) ."): ".implode(", ", $failCodes);
$success = false;
}
else
{
$msg = "Import items successful";
}
echo json_encode( array('success'=>$success,'message'=>$msg) );
}
/*
get the width for the add/edit form
*/
function get_form_width()
{
return 360;
}
function is_sale_store_item($item_number)
{
echo $this->Item->is_sale_store_item_exist($item_number);
}
}
?>

View File

@@ -1,278 +0,0 @@
<?php
require_once ("secure_area.php");
class Receivings extends Secure_area
{
function __construct()
{
parent::__construct('receivings');
$this->load->library('receiving_lib');
}
function index()
{
$this->_reload();
}
function item_search()
{
$suggestions = $this->Item->get_item_search_suggestions($this->input->post('q'),$this->input->post('limit'));
$suggestions = array_merge($suggestions, $this->Item_kit->get_item_kit_search_suggestions($this->input->post('q'),$this->input->post('limit')));
echo implode("\n",$suggestions);
}
function supplier_search()
{
$suggestions = $this->Supplier->get_suppliers_search_suggestions($this->input->post('q'),$this->input->post('limit'));
echo implode("\n",$suggestions);
}
function select_supplier()
{
$supplier_id = $this->input->post("supplier");
$this->receiving_lib->set_supplier($supplier_id);
$this->_reload();
}
function change_mode()
{
$stock_destination = $this->input->post('stock_destination');
$stock_source = $this->input->post("stock_source");
if ((!$stock_source || $stock_source == $this->receiving_lib->get_stock_source()) &&
(!$stock_destination || $stock_destination == $this->receiving_lib->get_stock_destination()))
{
$this->receiving_lib->empty_cart();
$mode = $this->input->post("mode");
$this->receiving_lib->set_mode($mode);
}
else
{
$this->receiving_lib->set_stock_source($stock_source);
$this->receiving_lib->set_stock_destination($stock_destination);
}
$this->_reload();
}
function add()
{
$data=array();
$mode = $this->receiving_lib->get_mode();
$item_id_or_number_or_item_kit_or_receipt = $this->input->post("item");
$quantity = ($mode=="receive" or $mode=="requisition") ? 1:-1;
$item_location = $this->receiving_lib->get_stock_source();
if($this->receiving_lib->is_valid_receipt($item_id_or_number_or_item_kit_or_receipt) && $mode=='return')
{
$this->receiving_lib->return_entire_receiving($item_id_or_number_or_item_kit_or_receipt,$item_location);
}
elseif($this->receiving_lib->is_valid_item_kit($item_id_or_number_or_item_kit_or_receipt))
{
$this->receiving_lib->add_item_kit($item_id_or_number_or_item_kit_or_receipt,$item_location);
}
else
{
if(!$this->receiving_lib->add_item($item_id_or_number_or_item_kit_or_receipt,$quantity,$item_location))
$data['error']=$this->lang->line('recvs_unable_to_add_item');
}
$this->_reload($data);
}
function edit_item($item_id)
{
$data= array();
$this->form_validation->set_rules('price', 'lang:items_price', 'required|numeric');
$this->form_validation->set_rules('quantity', 'lang:items_quantity', 'required|integer');
$this->form_validation->set_rules('discount', 'lang:items_discount', 'required|integer');
$description = $this->input->post("description");
$serialnumber = $this->input->post("serialnumber");
$price = $this->input->post("price");
$quantity = $this->input->post("quantity");
$discount = $this->input->post("discount");
$item_location = $this->input->post("location");
if ($this->form_validation->run() != FALSE)
{
$this->receiving_lib->edit_item($item_id,$description,$serialnumber,$quantity,$discount,$price);
}
else
{
$data['error']=$this->lang->line('recvs_error_editing_item');
}
$this->_reload($data);
}
function edit_item_unit($item_id)
{
$data= array();
$this->form_validation->set_rules('quantity', 'lang:items_quantity', 'required|integer');
$quantity = $this->input->post("quantity");
if ($this->form_validation->run() != FALSE)
{
$this->receiving_lib->edit_item_unit($item_id,$description,$quantity,0,0);
}
else
{
$data['error']=$this->lang->line('recvs_error_editing_item');
}
$this->_reload($data);
}
function delete_item($item_number)
{
$this->receiving_lib->delete_item($item_number);
$this->_reload();
}
function delete_supplier()
{
$this->receiving_lib->delete_supplier();
$this->_reload();
}
function complete()
{
$data['cart']=$this->receiving_lib->get_cart();
$data['total']=$this->receiving_lib->get_total();
$data['receipt_title']=$this->lang->line('recvs_receipt');
$data['transaction_time']= date('m/d/Y h:i:s a');
$data['mode']=$this->receiving_lib->get_mode();
$stock_locations = $this->Stock_locations->get_undeleted_all()->result_array();
$data['show_stock_locations'] = count($stock_locations) > 1;
$supplier_id=$this->receiving_lib->get_supplier();
$employee_id=$this->Employee->get_logged_in_employee_info()->person_id;
$comment = $this->input->post('comment');
$emp_info=$this->Employee->get_info($employee_id);
$payment_type = $this->input->post('payment_type');
$data['payment_type']=$this->input->post('payment_type');
$data['stock_location']=$this->receiving_lib->get_stock_source();
if ($this->input->post('amount_tendered'))
{
$data['amount_tendered'] = $this->input->post('amount_tendered');
$data['amount_change'] = to_currency($data['amount_tendered'] - round($data['total'], 2));
}
$data['employee']=$emp_info->first_name.' '.$emp_info->last_name;
if($supplier_id!=-1)
{
$suppl_info=$this->Supplier->get_info($supplier_id);
$data['supplier']=$suppl_info->first_name.' '.$suppl_info->last_name;
}
//SAVE receiving to database
$data['receiving_id']='RECV '.$this->Receiving->save($data['cart'], $supplier_id,$employee_id,$comment,$payment_type,$data['stock_location']);
if ($data['receiving_id'] == 'RECV -1')
{
$data['error_message'] = $this->lang->line('receivings_transaction_failed');
}
$this->load->view("receivings/receipt",$data);
$this->receiving_lib->clear_all();
$this->_remove_duplicate_cookies();
}
function requisition_complete()
{
if ($this->receiving_lib->get_stock_source() != $this->receiving_lib->get_stock_destination())
{
foreach($this->receiving_lib->get_cart() as $item)
{
$this->receiving_lib->delete_item($item['line']);
$this->receiving_lib->add_item($item['item_id'],$item['quantity'],$this->receiving_lib->get_stock_destination());
$this->receiving_lib->add_item($item['item_id'],-$item['quantity'],$this->receiving_lib->get_stock_source());
}
$this->complete();
}
else
{
$data['error']=$this->lang->line('recvs_error_requisition');
$this->_reload($data);
}
}
function receipt($receiving_id)
{
$receiving_info = $this->Receiving->get_info($receiving_id)->row_array();
$this->receiving_lib->copy_entire_receiving($receiving_id);
$data['cart']=$this->receiving_lib->get_cart();
$data['total']=$this->receiving_lib->get_total();
$data['mode']=$this->receiving_lib->get_mode();
$data['receipt_title']=$this->lang->line('recvs_receipt');
$data['transaction_time']= date('m/d/Y h:i:s a', strtotime($receiving_info['receiving_time']));
$stock_locations = $this->Stock_locations->get_undeleted_all()->result_array();
$data['show_stock_locations'] = count($stock_locations) > 1;
$supplier_id=$this->receiving_lib->get_supplier();
$emp_info=$this->Employee->get_info($receiving_info['employee_id']);
$data['payment_type']=$receiving_info['payment_type'];
$data['employee']=$emp_info->first_name.' '.$emp_info->last_name;
if($supplier_id!=-1)
{
$supplier_info=$this->Supplier->get_info($supplier_id);
$data['supplier']=$supplier_info->first_name.' '.$supplier_info->last_name;
}
$data['receiving_id']='RECV '.$receiving_id;
$this->load->view("receivings/receipt",$data);
$this->receiving_lib->clear_all();
$this->_remove_duplicate_cookies();
}
function _reload($data=array())
{
$data['stock_locations'] = array();
$stock_locations = $this->Stock_locations->get_undeleted_all()->result_array();
$show_stock_locations = count($stock_locations) > 1;
$person_info = $this->Employee->get_logged_in_employee_info();
$data['cart']=$this->receiving_lib->get_cart();
$data['modes']=array('receive'=>$this->lang->line('recvs_receiving'),'return'=>$this->lang->line('recvs_return'));
$data['mode']=$this->receiving_lib->get_mode();
if ($show_stock_locations) {
$data['modes']['requisition'] = $this->lang->line('recvs_requisition');
foreach($stock_locations as $location_data)
{
$data['stock_locations'][$location_data['location_id']] = $location_data['location_name'];
}
$data['stock_source']=$this->receiving_lib->get_stock_source();
$data['stock_destination']=$this->receiving_lib->get_stock_destination();
}
$data['show_stock_locations'] = $show_stock_locations;
$data['total']=$this->receiving_lib->get_total();
$data['items_module_allowed'] = $this->Employee->has_permission('items', $person_info->person_id);
$data['payment_options']=array(
$this->lang->line('sales_cash') => $this->lang->line('sales_cash'),
$this->lang->line('sales_check') => $this->lang->line('sales_check'),
$this->lang->line('sales_debit') => $this->lang->line('sales_debit'),
$this->lang->line('sales_credit') => $this->lang->line('sales_credit')
);
$supplier_id=$this->receiving_lib->get_supplier();
if($supplier_id!=-1)
{
$info=$this->Supplier->get_info($supplier_id);
$data['supplier']=$info->first_name.' '.$info->last_name;
}
$this->load->view("receivings/receiving",$data);
$this->_remove_duplicate_cookies();
}
function cancel_receiving()
{
$this->receiving_lib->clear_all();
$this->_reload();
}
}
?>

View File

@@ -1,470 +0,0 @@
<?php
require_once ("secure_area.php");
class Sales extends Secure_area
{
function __construct()
{
parent::__construct('sales');
$this->load->library('sale_lib');
}
function index()
{
$this->_reload();
}
function item_search()
{
$suggestions = $this->Item->get_item_search_suggestions($this->input->post('q'),$this->input->post('limit'));
$suggestions = array_merge($suggestions, $this->Item_kit->get_item_kit_search_suggestions($this->input->post('q'),$this->input->post('limit')));
echo implode("\n",$suggestions);
}
function customer_search()
{
$suggestions = $this->Customer->get_customer_search_suggestions($this->input->post('q'),$this->input->post('limit'));
echo implode("\n",$suggestions);
}
function select_customer()
{
$customer_id = $this->input->post("customer");
$this->sale_lib->set_customer($customer_id);
$this->_reload();
}
function change_mode()
{
$stock_location = $this->input->post("stock_location");
if (!$stock_location || $stock_location == $this->sale_lib->get_sale_location())
{
$this->sale_lib->clear_all();
$mode = $this->input->post("mode");
$this->sale_lib->set_mode($mode);
}
else
{
$this->sale_lib->set_sale_location($stock_location);
}
$this->_reload();
}
function set_comment()
{
$this->sale_lib->set_comment($this->input->post('comment'));
}
function set_email_receipt()
{
$this->sale_lib->set_email_receipt($this->input->post('email_receipt'));
}
//Alain Multiple Payments
function add_payment()
{
$data = array();
$this->form_validation->set_rules( 'amount_tendered', 'lang:sales_amount_tendered', 'numeric' );
if ( $this->form_validation->run() == FALSE )
{
if ( $this->input->post( 'payment_type' ) == $this->lang->line( 'sales_gift_card' ) )
$data['error']=$this->lang->line('sales_must_enter_numeric_giftcard');
else
$data['error']=$this->lang->line('sales_must_enter_numeric');
$this->_reload( $data );
return;
}
$payment_type = $this->input->post( 'payment_type' );
if ( $payment_type == $this->lang->line( 'sales_giftcard' ) )
{
$payments = $this->sale_lib->get_payments();
$payment_type = $this->input->post( 'payment_type' ) . ':' . $payment_amount = $this->input->post( 'amount_tendered' );
$current_payments_with_giftcard = isset( $payments[$payment_type] ) ? $payments[$payment_type]['payment_amount'] : 0;
$cur_giftcard_value = $this->Giftcard->get_giftcard_value( $this->input->post( 'amount_tendered' ) ) - $current_payments_with_giftcard;
if ( $cur_giftcard_value <= 0 )
{
$data['error'] = 'Giftcard balance is ' . to_currency( $this->Giftcard->get_giftcard_value( $this->input->post( 'amount_tendered' ) ) ) . ' !';
$this->_reload( $data );
return;
}
$new_giftcard_value = $this->Giftcard->get_giftcard_value( $this->input->post( 'amount_tendered' ) ) - $this->sale_lib->get_amount_due( );
$new_giftcard_value = ( $new_giftcard_value >= 0 ) ? $new_giftcard_value : 0;
$data['warning'] = 'Giftcard ' . $this->input->post( 'amount_tendered' ) . ' balance is ' . to_currency( $new_giftcard_value ) . ' !';
$payment_amount = min( $this->sale_lib->get_amount_due( ), $this->Giftcard->get_giftcard_value( $this->input->post( 'amount_tendered' ) ) );
}
else
{
$payment_amount = $this->input->post( 'amount_tendered' );
}
if( !$this->sale_lib->add_payment( $payment_type, $payment_amount ) )
{
$data['error']='Unable to Add Payment! Please try again!';
}
$this->_reload($data);
}
//Alain Multiple Payments
function delete_payment( $payment_id )
{
$this->sale_lib->delete_payment( $payment_id );
$this->_reload();
}
function add()
{
$data=array();
$mode = $this->sale_lib->get_mode();
$item_id_or_number_or_item_kit_or_receipt = $this->input->post("item");
$quantity = ($mode=="return")? -1:1;
$item_location = $this->sale_lib->get_sale_location();
if($this->sale_lib->is_valid_receipt($item_id_or_number_or_item_kit_or_receipt) && $mode=='return')
{
$this->sale_lib->return_entire_sale($item_id_or_number_or_item_kit_or_receipt);
}
elseif($this->sale_lib->is_valid_item_kit($item_id_or_number_or_item_kit_or_receipt))
{
$this->sale_lib->add_item_kit($item_id_or_number_or_item_kit_or_receipt,$item_location);
}
elseif(!$this->sale_lib->add_item($item_id_or_number_or_item_kit_or_receipt,$quantity,$item_location))
{
$data['error']=$this->lang->line('sales_unable_to_add_item');
}
if($this->sale_lib->out_of_stock($item_id_or_number_or_item_kit_or_receipt,$item_location))
{
$data['warning'] = $this->lang->line('sales_quantity_less_than_zero');
}
$this->_reload($data);
}
function edit_item($line)
{
$data= array();
$this->form_validation->set_rules('price', 'lang:items_price', 'required|numeric');
$this->form_validation->set_rules('quantity', 'lang:items_quantity', 'required|numeric');
$description = $this->input->post("description");
$serialnumber = $this->input->post("serialnumber");
$price = $this->input->post("price");
$quantity = $this->input->post("quantity");
$discount = $this->input->post("discount");
$item_location = $this->input->post("location");
if ($this->form_validation->run() != FALSE)
{
$this->sale_lib->edit_item($line,$description,$serialnumber,$quantity,$discount,$price);
}
else
{
$data['error']=$this->lang->line('sales_error_editing_item');
}
if($this->sale_lib->out_of_stock($this->sale_lib->get_item_id($line),$item_location))
{
$data['warning'] = $this->lang->line('sales_quantity_less_than_zero');
}
$this->_reload($data);
}
function delete_item($item_number)
{
$this->sale_lib->delete_item($item_number);
$this->_reload();
}
function remove_customer()
{
$this->sale_lib->remove_customer();
$this->_reload();
}
function complete()
{
$data['cart']=$this->sale_lib->get_cart();
$data['subtotal']=$this->sale_lib->get_subtotal();
$data['taxes']=$this->sale_lib->get_taxes();
$data['total']=$this->sale_lib->get_total();
$data['receipt_title']=$this->lang->line('sales_receipt');
$data['transaction_time']= date('m/d/Y h:i:s a');
$stock_locations = $this->Stock_locations->get_undeleted_all()->result_array();
$data['show_stock_locations'] = count($stock_locations) > 1;
$customer_id=$this->sale_lib->get_customer();
$employee_id=$this->Employee->get_logged_in_employee_info()->person_id;
$comment = $this->sale_lib->get_comment();
$emp_info=$this->Employee->get_info($employee_id);
$data['payments']=$this->sale_lib->get_payments();
$data['amount_change']=to_currency($this->sale_lib->get_amount_due() * -1);
$data['employee']=$emp_info->first_name.' '.$emp_info->last_name;
if($customer_id!=-1)
{
$cust_info=$this->Customer->get_info($customer_id);
$data['customer']=$cust_info->first_name.' '.$cust_info->last_name;
}
//SAVE sale to database
$data['sale_id']='POS '.$this->Sale->save($data['cart'], $customer_id,$employee_id,$comment,$data['payments']);
if ($data['sale_id'] == 'POS -1')
{
$data['error_message'] = $this->lang->line('sales_transaction_failed');
}
else
{
if ($this->sale_lib->get_email_receipt() && !empty($cust_info->email))
{
$this->load->library('email');
$config['mailtype'] = 'html';
$this->email->initialize($config);
$this->email->from($this->config->item('email'), $this->config->item('company'));
$this->email->to($cust_info->email);
$this->email->subject($this->lang->line('sales_receipt'));
$this->email->message($this->load->view("sales/receipt_email",$data, true));
$this->email->send();
}
}
$this->load->view("sales/receipt",$data);
$this->sale_lib->clear_all();
$this->_remove_duplicate_cookies();
}
function receipt($sale_id)
{
$sale_info = $this->Sale->get_info($sale_id)->row_array();
$this->sale_lib->copy_entire_sale($sale_id);
$stock_locations = $this->Stock_locations->get_undeleted_all()->result_array();
$data['show_stock_locations'] = count($stock_locations) > 1;
$data['cart']=$this->sale_lib->get_cart();
$data['payments']=$this->sale_lib->get_payments();
$data['subtotal']=$this->sale_lib->get_subtotal();
$data['taxes']=$this->sale_lib->get_taxes();
$data['total']=$this->sale_lib->get_total();
$data['receipt_title']=$this->lang->line('sales_receipt');
$data['transaction_time']= date('m/d/Y h:i:s a', strtotime($sale_info['sale_time']));
$customer_id=$this->sale_lib->get_customer();
$emp_info=$this->Employee->get_info($sale_info['employee_id']);
$data['payment_type']=$sale_info['payment_type'];
$data['amount_change']=to_currency($this->sale_lib->get_amount_due() * -1);
$data['employee']=$emp_info->first_name.' '.$emp_info->last_name;
if($customer_id!=-1)
{
$cust_info=$this->Customer->get_info($customer_id);
$data['customer']=$cust_info->first_name.' '.$cust_info->last_name;
}
$data['sale_id']='POS '.$sale_id;
$this->load->view("sales/receipt",$data);
$this->sale_lib->clear_all();
$this->_remove_duplicate_cookies();
}
function edit($sale_id)
{
$data = array();
$data['customers'] = array('' => 'No Customer');
foreach ($this->Customer->get_all()->result() as $customer)
{
$data['customers'][$customer->person_id] = $customer->first_name . ' '. $customer->last_name;
}
$data['employees'] = array();
foreach ($this->Employee->get_all()->result() as $employee)
{
$data['employees'][$employee->person_id] = $employee->first_name . ' '. $employee->last_name;
}
$sale_info = $this->Sale->get_info($sale_id)->row_array();
$person_name = $sale_info['first_name'] . " " . $sale_info['last_name'];
$data['selected_customer'] = !empty($sale_info['customer_id']) ? $sale_info['customer_id'] . "|" . $person_name : "";
$data['sale_info'] = $sale_info;
$this->load->view('sales/form', $data);
}
function delete($sale_id = -1, $update_inventory=TRUE) {
$employee_id=$this->Employee->get_logged_in_employee_info()->person_id;
$sale_ids= $sale_id == -1 ? $this->input->post('ids') : array($sale_id);
if($this->Sale->delete_list($sale_ids, $employee_id, $update_inventory))
{
echo json_encode(array('success'=>true,'message'=>$this->lang->line('sales_delete_successful').' '.
count($sale_ids).' '.$this->lang->line('sales_one_or_multiple'),'ids'=>$sale_ids));
}
else
{
echo json_encode(array('success'=>false,'message'=>$this->lang->line('sales_delete_unsuccessful')));
}
}
function save($sale_id)
{
$sale_data = array(
'sale_time' => date('Y-m-d', strtotime($this->input->post('date'))),
'customer_id' => $this->input->post('customer_id') ? $this->input->post('customer_id') : null,
'employee_id' => $this->input->post('employee_id'),
'comment' => $this->input->post('comment')
);
if ($this->Sale->update($sale_data, $sale_id))
{
echo json_encode(array(
'success'=>true,
'message'=>$this->lang->line('sales_successfully_updated'),
'id'=>$sale_id)
);
}
else
{
echo json_encode(array(
'success'=>false,
'message'=>$this->lang->line('sales_unsuccessfully_updated'),
'id'=>$sale_id)
);
}
}
function _payments_cover_total()
{
$total_payments = 0;
foreach($this->sale_lib->get_payments() as $payment)
{
$total_payments += $payment['payment_amount'];
}
/* Changed the conditional to account for floating point rounding */
if ( ($this->sale_lib->get_mode() == 'sale') &&
( ( to_currency_no_money( $this->sale_lib->get_total() ) - $total_payments ) > 1e-6 ) )
{
return false;
}
return true;
}
function _reload($data=array())
{
$person_info = $this->Employee->get_logged_in_employee_info();
$data['cart']=$this->sale_lib->get_cart();
$data['modes']=array('sale'=>$this->lang->line('sales_sale'),'return'=>$this->lang->line('sales_return'));
$data['mode']=$this->sale_lib->get_mode();
$data['stock_locations'] = array();
$stock_locations = $this->Stock_locations->get_undeleted_all()->result_array();
$show_stock_locations = count($stock_locations) > 1;
if ($show_stock_locations) {
foreach($stock_locations as $location_data)
{
$data['stock_locations'][$location_data['location_id']] = $location_data['location_name'];
}
$data['stock_location']=$this->sale_lib->get_sale_location();
}
$data['show_stock_locations'] = $show_stock_locations;
$data['subtotal']=$this->sale_lib->get_subtotal();
$data['taxes']=$this->sale_lib->get_taxes();
$data['total']=$this->sale_lib->get_total();
$data['items_module_allowed'] = $this->Employee->has_permission('items', $person_info->person_id);
$data['comment'] = $this->sale_lib->get_comment();
$data['email_receipt'] = $this->sale_lib->get_email_receipt();
$data['payments_total']=$this->sale_lib->get_payments_total();
$data['amount_due']=$this->sale_lib->get_amount_due();
$data['payments']=$this->sale_lib->get_payments();
$data['payment_options']=array(
$this->lang->line('sales_cash') => $this->lang->line('sales_cash'),
$this->lang->line('sales_check') => $this->lang->line('sales_check'),
$this->lang->line('sales_giftcard') => $this->lang->line('sales_giftcard'),
$this->lang->line('sales_debit') => $this->lang->line('sales_debit'),
$this->lang->line('sales_credit') => $this->lang->line('sales_credit')
);
$customer_id=$this->sale_lib->get_customer();
if($customer_id!=-1)
{
$info=$this->Customer->get_info($customer_id);
$data['customer']=$info->first_name.' '.$info->last_name;
$data['customer_email']=$info->email;
}
$data['payments_cover_total'] = $this->_payments_cover_total();
$this->load->view("sales/register",$data);
$this->_remove_duplicate_cookies();
}
function cancel_sale()
{
$this->sale_lib->clear_all();
$this->_reload();
}
function suspend()
{
$data['cart']=$this->sale_lib->get_cart();
$data['subtotal']=$this->sale_lib->get_subtotal();
$data['taxes']=$this->sale_lib->get_taxes();
$data['total']=$this->sale_lib->get_total();
$data['receipt_title']=$this->lang->line('sales_receipt');
$data['transaction_time']= date('m/d/Y h:i:s a');
$customer_id=$this->sale_lib->get_customer();
$employee_id=$this->Employee->get_logged_in_employee_info()->person_id;
$comment = $this->input->post('comment');
$emp_info=$this->Employee->get_info($employee_id);
$payment_type = $this->input->post('payment_type');
$data['payment_type']=$this->input->post('payment_type');
//Alain Multiple payments
$data['payments']=$this->sale_lib->get_payments();
$data['amount_change']=to_currency($this->sale_lib->get_amount_due() * -1);
$data['employee']=$emp_info->first_name.' '.$emp_info->last_name;
if($customer_id!=-1)
{
$cust_info=$this->Customer->get_info($customer_id);
$data['customer']=$cust_info->first_name.' '.$cust_info->last_name;
}
$total_payments = 0;
foreach($data['payments'] as $payment)
{
$total_payments += $payment['payment_amount'];
}
//SAVE sale to database
$data['sale_id']='POS '.$this->Sale_suspended->save($data['cart'], $customer_id,$employee_id,$comment,$data['payments']);
if ($data['sale_id'] == 'POS -1')
{
$data['error_message'] = $this->lang->line('sales_transaction_failed');
}
$this->sale_lib->clear_all();
$this->_reload(array('success' => $this->lang->line('sales_successfully_suspended_sale')));
}
function suspended()
{
$data = array();
$data['suspended_sales'] = $this->Sale_suspended->get_all()->result_array();
$this->load->view('sales/suspended', $data);
}
function unsuspend()
{
$sale_id = $this->input->post('suspended_sale_id');
$this->sale_lib->clear_all();
$this->sale_lib->copy_entire_suspended_sale($sale_id);
$this->Sale_suspended->delete($sale_id);
$this->_reload();
}
}
?>

View File

@@ -1,82 +0,0 @@
<?php
class Secure_area extends CI_Controller
{
/*
Controllers that are considered secure extend Secure_area, optionally a $module_id can
be set to also check if a user can access a particular module in the system.
*/
function __construct($module_id=null)
{
parent::__construct();
$this->load->model('Employee');
if(!$this->Employee->is_logged_in())
{
redirect('login');
}
if(!$this->Employee->has_permission($module_id,$this->Employee->get_logged_in_employee_info()->person_id))
{
redirect('no_access/'.$module_id);
}
//load up global data
$logged_in_employee_info=$this->Employee->get_logged_in_employee_info();
$data['allowed_modules']=$this->Module->get_allowed_modules($logged_in_employee_info->person_id);
$data['user_info']=$logged_in_employee_info;
$this->load->vars($data);
}
function _remove_duplicate_cookies ()
{
//php < 5.3 doesn't have header remove so this function will fatal error otherwise
if (function_exists('header_remove'))
{
$CI = &get_instance();
// clean up all the cookies that are set...
$headers = headers_list();
$cookies_to_output = array ();
$header_session_cookie = '';
$session_cookie_name = $CI->config->item('sess_cookie_name');
foreach ($headers as $header)
{
list ($header_type, $data) = explode (':', $header, 2);
$header_type = trim ($header_type);
$data = trim ($data);
if (strtolower ($header_type) == 'set-cookie')
{
header_remove ('Set-Cookie');
$cookie_value = current(explode (';', $data));
list ($key, $val) = explode ('=', $cookie_value);
$key = trim ($key);
if ($key == $session_cookie_name)
{
// OVERWRITE IT (yes! do it!)
$header_session_cookie = $data;
continue;
}
else
{
// Not a session related cookie, add it as normal. Might be a CSRF or some other cookie we are setting
$cookies_to_output[] = array ('header_type' => $header_type, 'data' => $data);
}
}
}
if ( ! empty ($header_session_cookie))
{
$cookies_to_output[] = array ('header_type' => 'Set-Cookie', 'data' => $header_session_cookie);
}
foreach ($cookies_to_output as $cookie)
{
header ("{$cookie['header_type']}: {$cookie['data']}", false);
}
}
}
}
?>

View File

@@ -0,0 +1,96 @@
<?php (defined('BASEPATH')) OR exit('No direct script access allowed');
class MY_Lang extends CI_Lang
{
function __construct()
{
parent::__construct();
}
function switch_to( $idiom )
{
$CI =& get_instance();
if( is_string( $idiom ) )
{
$CI->config->set_item( 'language', $idiom );
$loaded = $this->is_loaded;
$this->is_loaded = array();
foreach($loaded as $file)
{
$this->load( str_replace( '_lang.php', '', $file ) );
}
}
}
/**
* Fetch a single line of text from the language array. Takes variable number
* of arguments and supports wildcards in the form of '%1', '%2', etc.
* Overloaded function.
*
* @access public
* @return mixed false if not found or the language string
*/
function line($line = '', $log_errors = true)
{
//get the arguments passed to the function
$args = func_get_args();
//count the number of arguments
$c = count($args);
//if one or more arguments, perform the necessary processing
if ($c)
{
//first argument should be the actual language line key
//so remove it from the array (pop from front)
$line = array_shift($args);
//check to make sure the key is valid and load the line
if ($line == '')
{
$line = FALSE;
}
else
{
if (isset($this->language[$line]))
{
$line = $this->language[$line];
//if the line exists and more function arguments remain
//perform wildcard replacements
if ($args)
{
$i = 1;
foreach ($args as $arg)
{
$line = preg_replace('/\%'.$i.'/', $arg, $line);
$i++;
}
}
}
else
{
// just return label name (with TBD)
$line = $this->line_tbd($line);
log_message('error', 'Could not find the language line "'.$line.'"');
}
}
}
else
{
//if no arguments given, no language line available
$line = false;
}
return $line;
}
function line_tbd($line='')
{
return $line . ' (TBD)';
}
}
?>

View File

@@ -1,3 +1,4 @@
<!DOCTYPE html>
<html>
<head>
<title>403 Forbidden</title>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
</html>
</html>

View File

@@ -1,10 +0,0 @@
<div style="border:1px solid #990000;padding-left:20px;margin:0 0 10px 0;">
<h4>A PHP Error was encountered</h4>
<p>Severity: <?php echo $severity; ?></p>
<p>Message: <?php echo $message; ?></p>
<p>Filename: <?php echo $filepath; ?></p>
<p>Line Number: <?php echo $line; ?></p>
</div>

View File

@@ -1,28 +1,29 @@
<?php
/** GARRISON MODIFIED 4/20/2013 **/
function to_currency($number)
function to_currency($number,$escape=FALSE)
{
$CI =& get_instance();
$currency_symbol = $CI->config->item('currency_symbol') ? $CI->config->item('currency_symbol') : '$';
$currency_symbol = $currency_symbol == '$' && $escape ? '\$' : $currency_symbol;
$thousands_separator = $CI->config->item('thousands_separator') ? $CI->config->item('thousands_separator') : '';
$decimal_point = $CI->config->item('decimal_point') ? $CI->config->item('decimal_point') : '.';
if($number >= 0)
{
if($CI->config->item('currency_side') !== 'currency_side')
return $currency_symbol.number_format($number, 2, '.', '');
return $currency_symbol.number_format($number, 2, $decimal_point, $thousands_separator);
else
return number_format($number, 2, '.', '').$currency_symbol;
return number_format($number, 2, $decimal_point, $thousands_separator).$currency_symbol;
}
else
{
if($CI->config->item('currency_side') !== 'currency_side')
return '-'.$currency_symbol.number_format(abs($number), 2, '.', '');
return '-'.$currency_symbol.number_format(abs($number), 2, $decimal_point, $thousands_separator);
else
return '-'.number_format(abs($number), 2, '.', '').$currency_symbol;
return '-'.number_format(abs($number), 2, $decimal_point, $thousands_separator).$currency_symbol;
}
}
/** END MODIFIED **/
function to_currency_no_money($number)
{
return number_format($number, 2, '.', '');
}
?>
?>

View File

@@ -0,0 +1,69 @@
<?php
/*
* Matches each symbol of PHP date format standard
* with jQuery equivalent codeword
* @author Tristan Jahier
*/
function dateformat_jquery($php_format)
{
$SYMBOLS_MATCHING = array(
// Day
'd' => 'dd',
'D' => 'D',
'j' => 'd',
'l' => 'DD',
'N' => '',
'S' => '',
'w' => '',
'z' => 'o',
// Week
'W' => '',
// Month
'F' => 'MM',
'm' => 'mm',
'M' => 'M',
'n' => 'm',
't' => '',
// Year
'L' => '',
'o' => '',
'Y' => 'yy',
'y' => 'y',
// Time
'a' => 'tt',
'A' => 'TT',
'B' => '',
'g' => 'h',
'G' => 'H',
'h' => 'hh',
'H' => 'HH',
'i' => 'mm',
's' => 'ss',
'u' => ''
);
$jqueryui_format = "";
$escaping = false;
for($i = 0; $i < strlen($php_format); $i++)
{
$char = $php_format[$i];
if($char === '\\') // PHP date format escaping character
{
$i++;
if($escaping) $jqueryui_format .= $php_format[$i];
else $jqueryui_format .= '\'' . $php_format[$i];
$escaping = true;
}
else
{
if($escaping) { $jqueryui_format .= "'"; $escaping = false; }
if(isset($SYMBOLS_MATCHING[$char]))
$jqueryui_format .= $SYMBOLS_MATCHING[$char];
else
$jqueryui_format .= $char;
}
}
return $jqueryui_format;
}
?>

View File

@@ -0,0 +1,76 @@
# How to contribute
- [Getting help](#getting-help)
- [Submitting bug reports](#submitting-bug-reports)
- [Contributing code](#contributing-code)
## Getting help
Community discussion, questions, and informal bug reporting is done on the
[dompdf Google group](http://groups.google.com/group/dompdf). You may also
seek help on
[StackOverflow](http://stackoverflow.com/questions/tagged/dompdf).
## Submitting bug reports
The preferred way to report bugs is to use the
[GitHub issue tracker](http://github.com/dompdf/dompdf/issues). Before
reporting a bug, read these pointers.
**Please search inside the bug tracker to see if the bug you found is not already reported.**
**Note:** The issue tracker is for *bugs* and *feature requests*, not requests for help.
Questions should be asked on the
[dompdf Google group](http://groups.google.com/group/dompdf) instead.
### Reporting bugs effectively
- dompdf is maintained by volunteers. They don't owe you anything, so be
polite. Reports with an indignant or belligerent tone tend to be moved to the
bottom of the pile.
- Include information about **the PHP version on which the problem occurred**. Even
if you tested several PHP version on different servers, and the problem occurred
in all of them, mention this fact in the bug report.
Also include the operating system it's installed on. PHP configuration can also help,
and server error logs (like Apache logs)
- Mention which release of dompdf you're using (the zip, the master branch, etc).
Preferably, try also with the current development snapshot, to ensure the
problem has not already been fixed.
- Mention very precisely what went wrong. "X is broken" is not a good bug
report. What did you expect to happen? What happened instead? Describe the
exact steps a maintainer has to take to make the problem occur. We can not
fix something that we can not observe.
- If the problem can not be reproduced in any of the demos included in the
dompdf distribution, please provide an HTML document that demonstrates
the problem. There are a few options to show us your code:
- [JS Fiddle](http://jsfiddle.net/)
- [dompdf debug helper](http://eclecticgeek.com/dompdf/debug.php) (provided by @bsweeney)
- Include the HTML/CSS inside the bug report, with
[code highlighting](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#wiki-code).
## Contributing code
- Make sure you have a [GitHub Account](https://github.com/signup/free)
- Fork [dompdf](https://github.com/dompdf/dompdf/)
([how to fork a repo](https://help.github.com/articles/fork-a-repo))
- *Make your changes on the dev branch* or the most appropriate feature branch. Please only patch
the master branch if you are attempting to address an urgent bug in the released code.
- Add a simple test file in `www/test/`, with a comprehensive name.
- Submit a pull request
([how to create a pull request](https://help.github.com/articles/fork-a-repo))
### Coding standards
- 2 spaces per indentation level, no tabs.
- spaces inside `if` like this:
```php
if ( $foo == "bar" ) {
//
}
```
- booleans in lowercase
- opening braces *always* on the same line

View File

@@ -0,0 +1,456 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.

View File

@@ -0,0 +1,131 @@
**dompdf is an HTML to PDF converter**.
At its heart, dompdf is (mostly) [CSS 2.1](http://www.w3.org/TR/CSS2/) compliant
HTML layout and rendering engine written in PHP. It is a style-driven renderer:
it will download and read external stylesheets, inline style tags, and the style
attributes of individual HTML elements. It also supports most presentational
HTML attributes.
----
**Check out the [Demo](http://pxd.me/dompdf/www/examples.php) and ask any
question on [StackOverflow](http://stackoverflow.com/questions/tagged/dompdf) or
on the [Google Groups](http://groups.google.com/group/dompdf)**
----
[![Follow us on Twitter](http://twitter-badges.s3.amazonaws.com/twitter-a.png)](http://www.twitter.com/dompdf)
[![Follow us on Google+](https://ssl.gstatic.com/images/icons/gplus-32.png)](https://plus.google.com/108710008521858993320?prsrc=3)
Features
========
* handles most CSS 2.1 and a few CSS3 properties, including @import, @media &
@page rules
* supports most presentational HTML 4.0 attributes
* supports external stylesheets, either local or through http/ftp (via
fopen-wrappers)
* supports complex tables, including row & column spans, separate & collapsed
border models, individual cell styling
* image support (gif, png (8, 24 and 32 bit with alpha channel), bmp & jpeg)
* no dependencies on external PDF libraries, thanks to the R&OS PDF class
* inline PHP support
Requirements
============
* PHP 5.0+ (5.3+ recommended)
* DOM extension
* GD extension
Recommendations
============
* MBString extension: provides internationalization support. This extension is
*not* enabled by default. dompdf has limited internationalization support
when this extension is not enabled.
* opcache (OPcache, XCache, APC, etc.): improves performance
About Fonts & Character Encoding
============
PDF documents internally support the following fonts: Helvetica, Times-Roman,
Courier, Zapf-Dingbats, & Symbol. These fonts only support Windows ANSI
encoding. In order for a PDF to display characters that are not available in
Windows ANSI you must supply an external font. dompdf will embed any referenced
font in the PDF so long as it has been pre-loaded or is accessible to dompdf and
reference in CSS @font-face rules. See the
[font overview](https://github.com/dompdf/dompdf/wiki/About-Fonts-and-Character-Encoding)
for more information on how to use fonts.
The [DejaVu TrueType fonts](http://dejavu-fonts.org) have been pre-installed to
give dompdf decent Unicode character coverage by default. To use the DejaVu
fonts reference the font in your stylesheet, e.g. `body { font-family: Deja Vu
Sans; }` (for DejaVu Sans).
Easy Installation
============
Install with git
---
From the command line switch to the directory where dompdf will reside and run
the following commands:
```sh
git clone https://github.com/dompdf/dompdf.git
git submodule init
git submodule update
```
Install with composer
---
To install with Composer, simply add the requirement to your `composer.json`
file:
```json
{
"require" : {
"dompdf/dompdf" : "0.6.*"
}
}
```
And run Composer to update your dependencies:
```bash
$ curl -sS http://getcomposer.org/installer | php
$ php composer.phar update
```
Before you can use the Composer installation of DOMPDF in your application you
must disable dompdf's default auto-loader, include the Composer autoloader, and
load the dompdf configuration file:
```php
// somewhere early in your project's loading, require the Composer autoloader
// see: http://getcomposer.org/doc/00-intro.md
require 'vendor/autoload.php';
// disable DOMPDF's internal autoloader if you are using Composer
define('DOMPDF_ENABLE_AUTOLOAD', false);
// include DOMPDF's default configuration
require_once '/path/to/vendor/dompdf/dompdf/dompdf_config.inc.php';
```
Download and install
---
Download an archive of dompdf and extract it into the directory where dompdf
will reside
* You can download stable copies of dompdf from
https://github.com/dompdf/dompdf/tags
* Or download a nightly (the latest, unreleased code) from
http://eclecticgeek.com/dompdf
Limitations (Known Issues)
==========================
* not particularly tolerant to poorly-formed HTML input. To avoid any
unexpected rendering issues you should either enable the built-in HTML5
parser (via the `DOMPDF_ENABLE_HTML5PARSER` configuration constant) or run
your HTML through a HTML validator/cleaner (such as Tidy).
* large files or large tables can take a while to render
* CSS float is not supported (but is in the works, enable it through the
`DOMPDF_ENABLE_CSS_FLOAT` configuration constant).
* If you find this project useful, please consider making a donation.
(Any funds donated will be used to help further development on this project.)
[![Donate button](https://www.paypal.com/en_US/i/btn/btn_donate_SM.gif)](http://goo.gl/DSvWf)

View File

@@ -0,0 +1,23 @@
{
"name": "dompdf/dompdf",
"type": "library",
"description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter",
"homepage": "https://github.com/dompdf/dompdf",
"license": "LGPL",
"authors": [
{
"name": "Fabien Ménager",
"email": "fabien.menager@gmail.com"
},
{
"name": "Brian Sweeney",
"email": "eclecticgeek@gmail.com"
}
],
"autoload": {
"classmap": ["include/"]
},
"require": {
"phenx/php-font-lib": "0.2.*"
}
}

View File

@@ -0,0 +1,289 @@
<?php
/**
* Command line utility to use dompdf.
* Can also be used with HTTP GET parameters
*
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Display command line usage
*/
function dompdf_usage() {
$default_paper_size = DOMPDF_DEFAULT_PAPER_SIZE;
echo <<<EOD
Usage: {$_SERVER["argv"][0]} [options] html_file
html_file can be a filename, a url if fopen_wrappers are enabled, or the '-' character to read from standard input.
Options:
-h Show this message
-l List available paper sizes
-p size Paper size; something like 'letter', 'A4', 'legal', etc.
The default is '$default_paper_size'
-o orientation Either 'portrait' or 'landscape'. Default is 'portrait'
-b path Set the 'document root' of the html_file.
Relative urls (for stylesheets) are resolved using this directory.
Default is the directory of html_file.
-f file The output filename. Default is the input [html_file].pdf
-v Verbose: display html parsing warnings and file not found errors.
-d Very verbose: display oodles of debugging output: every frame
in the tree printed to stdout.
-t Comma separated list of debugging types (page-break,reflow,split)
EOD;
exit;
}
/**
* Parses command line options
*
* @return array The command line options
*/
function getoptions() {
$opts = array();
if ( $_SERVER["argc"] == 1 )
return $opts;
$i = 1;
while ($i < $_SERVER["argc"]) {
switch ($_SERVER["argv"][$i]) {
case "--help":
case "-h":
$opts["h"] = true;
$i++;
break;
case "-l":
$opts["l"] = true;
$i++;
break;
case "-p":
if ( !isset($_SERVER["argv"][$i+1]) )
die("-p switch requires a size parameter\n");
$opts["p"] = $_SERVER["argv"][$i+1];
$i += 2;
break;
case "-o":
if ( !isset($_SERVER["argv"][$i+1]) )
die("-o switch requires an orientation parameter\n");
$opts["o"] = $_SERVER["argv"][$i+1];
$i += 2;
break;
case "-b":
if ( !isset($_SERVER["argv"][$i+1]) )
die("-b switch requires a path parameter\n");
$opts["b"] = $_SERVER["argv"][$i+1];
$i += 2;
break;
case "-f":
if ( !isset($_SERVER["argv"][$i+1]) )
die("-f switch requires a filename parameter\n");
$opts["f"] = $_SERVER["argv"][$i+1];
$i += 2;
break;
case "-v":
$opts["v"] = true;
$i++;
break;
case "-d":
$opts["d"] = true;
$i++;
break;
case "-t":
if ( !isset($_SERVER['argv'][$i + 1]) )
die("-t switch requires a comma separated list of types\n");
$opts["t"] = $_SERVER['argv'][$i+1];
$i += 2;
break;
default:
$opts["filename"] = $_SERVER["argv"][$i];
$i++;
break;
}
}
return $opts;
}
require_once("dompdf_config.inc.php");
global $_dompdf_show_warnings, $_dompdf_debug, $_DOMPDF_DEBUG_TYPES;
$sapi = php_sapi_name();
$options = array();
switch ( $sapi ) {
case "cli":
$opts = getoptions();
if ( isset($opts["h"]) || (!isset($opts["filename"]) && !isset($opts["l"])) ) {
dompdf_usage();
exit;
}
if ( isset($opts["l"]) ) {
echo "\nUnderstood paper sizes:\n";
foreach (array_keys(CPDF_Adapter::$PAPER_SIZES) as $size)
echo " " . mb_strtoupper($size) . "\n";
exit;
}
$file = $opts["filename"];
if ( isset($opts["p"]) )
$paper = $opts["p"];
else
$paper = DOMPDF_DEFAULT_PAPER_SIZE;
if ( isset($opts["o"]) )
$orientation = $opts["o"];
else
$orientation = "portrait";
if ( isset($opts["b"]) )
$base_path = $opts["b"];
if ( isset($opts["f"]) )
$outfile = $opts["f"];
else {
if ( $file === "-" )
$outfile = "dompdf_out.pdf";
else
$outfile = str_ireplace(array(".html", ".htm", ".php"), "", $file) . ".pdf";
}
if ( isset($opts["v"]) )
$_dompdf_show_warnings = true;
if ( isset($opts["d"]) ) {
$_dompdf_show_warnings = true;
$_dompdf_debug = true;
}
if ( isset($opts['t']) ) {
$arr = split(',',$opts['t']);
$types = array();
foreach ($arr as $type)
$types[ trim($type) ] = 1;
$_DOMPDF_DEBUG_TYPES = $types;
}
$save_file = true;
break;
default:
if ( isset($_GET["input_file"]) )
$file = rawurldecode($_GET["input_file"]);
else
throw new DOMPDF_Exception("An input file is required (i.e. input_file _GET variable).");
if ( isset($_GET["paper"]) )
$paper = rawurldecode($_GET["paper"]);
else
$paper = DOMPDF_DEFAULT_PAPER_SIZE;
if ( isset($_GET["orientation"]) )
$orientation = rawurldecode($_GET["orientation"]);
else
$orientation = "portrait";
if ( isset($_GET["base_path"]) ) {
$base_path = rawurldecode($_GET["base_path"]);
$file = $base_path . $file; # Set the input file
}
if ( isset($_GET["options"]) ) {
$options = $_GET["options"];
}
$file_parts = explode_url($file);
/* Check to see if the input file is local and, if so, that the base path falls within that specified by DOMDPF_CHROOT */
if(($file_parts['protocol'] == '' || $file_parts['protocol'] === 'file://')) {
$file = realpath($file);
if ( strpos($file, DOMPDF_CHROOT) !== 0 ) {
throw new DOMPDF_Exception("Permission denied on $file. The file could not be found under the directory specified by DOMPDF_CHROOT.");
}
}
if($file_parts['protocol'] === 'php://') {
throw new DOMPDF_Exception("Permission denied on $file. This script does not allow PHP streams.");
}
$outfile = "dompdf_out.pdf"; # Don't allow them to set the output file
$save_file = false; # Don't save the file
break;
}
$dompdf = new DOMPDF();
if ( $file === "-" ) {
$str = "";
while ( !feof(STDIN) )
$str .= fread(STDIN, 4096);
$dompdf->load_html($str);
} else
$dompdf->load_html_file($file);
if ( isset($base_path) ) {
$dompdf->set_base_path($base_path);
}
$dompdf->set_paper($paper, $orientation);
$dompdf->render();
if ( $_dompdf_show_warnings ) {
global $_dompdf_warnings;
foreach ($_dompdf_warnings as $msg)
echo $msg . "\n";
echo $dompdf->get_canvas()->get_cpdf()->messages;
flush();
}
if ( $save_file ) {
// if ( !is_writable($outfile) )
// throw new DOMPDF_Exception("'$outfile' is not writable.");
if ( strtolower(DOMPDF_PDF_BACKEND) === "gd" )
$outfile = str_replace(".pdf", ".png", $outfile);
list($proto, $host, $path, $file) = explode_url($outfile);
if ( $proto != "" ) // i.e. not file://
$outfile = $file; // just save it locally, FIXME? could save it like wget: ./host/basepath/file
$outfile = realpath(dirname($outfile)) . DIRECTORY_SEPARATOR . basename($outfile);
if ( strpos($outfile, DOMPDF_CHROOT) !== 0 )
throw new DOMPDF_Exception("Permission denied.");
file_put_contents($outfile, $dompdf->output( array("compress" => 0) ));
exit(0);
}
if ( !headers_sent() ) {
$dompdf->stream($outfile, $options);
}

View File

@@ -0,0 +1,31 @@
<?php
//define("DOMPDF_TEMP_DIR", "/tmp");
//define("DOMPDF_CHROOT", DOMPDF_DIR);
//define("DOMPDF_FONT_DIR", DOMPDF_DIR."/lib/fonts/");
//define("DOMPDF_FONT_CACHE", DOMPDF_DIR."/lib/fonts/");
//define("DOMPDF_UNICODE_ENABLED", true);
//define("DOMPDF_PDF_BACKEND", "PDFLib");
//define("DOMPDF_DEFAULT_MEDIA_TYPE", "print");
//define("DOMPDF_DEFAULT_PAPER_SIZE", "letter");
//define("DOMPDF_DEFAULT_FONT", "serif");
//define("DOMPDF_DPI", 72);
//define("DOMPDF_ENABLE_PHP", true);
//define("DOMPDF_ENABLE_REMOTE", true);
//define("DOMPDF_ENABLE_CSS_FLOAT", true);
//define("DOMPDF_ENABLE_JAVASCRIPT", false);
//define("DEBUGPNG", true);
//define("DEBUGKEEPTEMP", true);
//define("DEBUGCSS", true);
//define("DEBUG_LAYOUT", true);
//define("DEBUG_LAYOUT_LINES", false);
//define("DEBUG_LAYOUT_BLOCKS", false);
//define("DEBUG_LAYOUT_INLINE", false);
//define("DOMPDF_FONT_HEIGHT_RATIO", 1.0);
//define("DEBUG_LAYOUT_PADDINGBOX", false);
//define("DOMPDF_LOG_OUTPUT_FILE", DOMPDF_FONT_DIR."log.htm");
//define("DOMPDF_ENABLE_HTML5PARSER", true);
//define("DOMPDF_ENABLE_FONTSUBSETTING", true);
// DOMPDF authentication
//define("DOMPDF_ADMIN_USERNAME", "user");
//define("DOMPDF_ADMIN_PASSWORD", "password");

View File

@@ -0,0 +1,393 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author Helmut Tischer <htischer@weihenstephan.org>
* @author Fabien Ménager <fabien.menager@gmail.com>
* @autho Brian Sweeney <eclecticgeek@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
if ( class_exists( 'DOMPDF' , false ) ) { return; }
PHP_VERSION >= 5.0 or die("DOMPDF requires PHP 5.0+");
/**
* The root of your DOMPDF installation
*/
define("DOMPDF_DIR", str_replace(DIRECTORY_SEPARATOR, '/', realpath(dirname(__FILE__))));
/**
* The location of the DOMPDF include directory
*/
define("DOMPDF_INC_DIR", DOMPDF_DIR . "/include");
/**
* The location of the DOMPDF lib directory
*/
define("DOMPDF_LIB_DIR", DOMPDF_DIR . "/lib");
/**
* Some installations don't have $_SERVER['DOCUMENT_ROOT']
* http://fyneworks.blogspot.com/2007/08/php-documentroot-in-iis-windows-servers.html
*/
if( !isset($_SERVER['DOCUMENT_ROOT']) ) {
$path = "";
if ( isset($_SERVER['SCRIPT_FILENAME']) )
$path = $_SERVER['SCRIPT_FILENAME'];
elseif ( isset($_SERVER['PATH_TRANSLATED']) )
$path = str_replace('\\\\', '\\', $_SERVER['PATH_TRANSLATED']);
$_SERVER['DOCUMENT_ROOT'] = str_replace( '\\', '/', substr($path, 0, 0-strlen($_SERVER['PHP_SELF'])));
}
/** Include the custom config file if it exists */
if ( file_exists(DOMPDF_DIR . "/dompdf_config.custom.inc.php") ){
require_once(DOMPDF_DIR . "/dompdf_config.custom.inc.php");
}
//FIXME: Some function definitions rely on the constants defined by DOMPDF. However, might this location prove problematic?
require_once(DOMPDF_INC_DIR . "/functions.inc.php");
/**
* Username and password used by the configuration utility in www/
*/
def("DOMPDF_ADMIN_USERNAME", "user");
def("DOMPDF_ADMIN_PASSWORD", "password");
/**
* The location of the DOMPDF font directory
*
* The location of the directory where DOMPDF will store fonts and font metrics
* Note: This directory must exist and be writable by the webserver process.
* *Please note the trailing slash.*
*
* Notes regarding fonts:
* Additional .afm font metrics can be added by executing load_font.php from command line.
*
* Only the original "Base 14 fonts" are present on all pdf viewers. Additional fonts must
* be embedded in the pdf file or the PDF may not display correctly. This can significantly
* increase file size unless font subsetting is enabled. Before embedding a font please
* review your rights under the font license.
*
* Any font specification in the source HTML is translated to the closest font available
* in the font directory.
*
* The pdf standard "Base 14 fonts" are:
* Courier, Courier-Bold, Courier-BoldOblique, Courier-Oblique,
* Helvetica, Helvetica-Bold, Helvetica-BoldOblique, Helvetica-Oblique,
* Times-Roman, Times-Bold, Times-BoldItalic, Times-Italic,
* Symbol, ZapfDingbats.
*/
def("DOMPDF_FONT_DIR", DOMPDF_DIR . "/lib/fonts/");
/**
* The location of the DOMPDF font cache directory
*
* This directory contains the cached font metrics for the fonts used by DOMPDF.
* This directory can be the same as DOMPDF_FONT_DIR
*
* Note: This directory must exist and be writable by the webserver process.
*/
def("DOMPDF_FONT_CACHE", DOMPDF_FONT_DIR);
/**
* The location of a temporary directory.
*
* The directory specified must be writeable by the webserver process.
* The temporary directory is required to download remote images and when
* using the PFDLib back end.
*/
def("DOMPDF_TEMP_DIR", sys_get_temp_dir());
/**
* ==== IMPORTANT ====
*
* dompdf's "chroot": Prevents dompdf from accessing system files or other
* files on the webserver. All local files opened by dompdf must be in a
* subdirectory of this directory. DO NOT set it to '/' since this could
* allow an attacker to use dompdf to read any files on the server. This
* should be an absolute path.
* This is only checked on command line call by dompdf.php, but not by
* direct class use like:
* $dompdf = new DOMPDF(); $dompdf->load_html($htmldata); $dompdf->render(); $pdfdata = $dompdf->output();
*/
def("DOMPDF_CHROOT", realpath(DOMPDF_DIR));
/**
* Whether to use Unicode fonts or not.
*
* When set to true the PDF backend must be set to "CPDF" and fonts must be
* loaded via load_font.php.
*
* When enabled, dompdf can support all Unicode glyphs. Any glyphs used in a
* document must be present in your fonts, however.
*/
def("DOMPDF_UNICODE_ENABLED", true);
/**
* Whether to enable font subsetting or not.
*/
def("DOMPDF_ENABLE_FONTSUBSETTING", true);
/**
* The PDF rendering backend to use
*
* Valid settings are 'PDFLib', 'CPDF' (the bundled R&OS PDF class), 'GD' and
* 'auto'. 'auto' will look for PDFLib and use it if found, or if not it will
* fall back on CPDF. 'GD' renders PDFs to graphic files. {@link
* Canvas_Factory} ultimately determines which rendering class to instantiate
* based on this setting.
*
* Both PDFLib & CPDF rendering backends provide sufficient rendering
* capabilities for dompdf, however additional features (e.g. object,
* image and font support, etc.) differ between backends. Please see
* {@link PDFLib_Adapter} for more information on the PDFLib backend
* and {@link CPDF_Adapter} and lib/class.pdf.php for more information
* on CPDF. Also see the documentation for each backend at the links
* below.
*
* The GD rendering backend is a little different than PDFLib and
* CPDF. Several features of CPDF and PDFLib are not supported or do
* not make any sense when creating image files. For example,
* multiple pages are not supported, nor are PDF 'objects'. Have a
* look at {@link GD_Adapter} for more information. GD support is
* experimental, so use it at your own risk.
*
* @link http://www.pdflib.com
* @link http://www.ros.co.nz/pdf
* @link http://www.php.net/image
*/
def("DOMPDF_PDF_BACKEND", "CPDF");
/**
* PDFlib license key
*
* If you are using a licensed, commercial version of PDFlib, specify
* your license key here. If you are using PDFlib-Lite or are evaluating
* the commercial version of PDFlib, comment out this setting.
*
* @link http://www.pdflib.com
*
* If pdflib present in web server and auto or selected explicitely above,
* a real license code must exist!
*/
//def("DOMPDF_PDFLIB_LICENSE", "your license key here");
/**
* html target media view which should be rendered into pdf.
* List of types and parsing rules for future extensions:
* http://www.w3.org/TR/REC-html40/types.html
* screen, tty, tv, projection, handheld, print, braille, aural, all
* Note: aural is deprecated in CSS 2.1 because it is replaced by speech in CSS 3.
* Note, even though the generated pdf file is intended for print output,
* the desired content might be different (e.g. screen or projection view of html file).
* Therefore allow specification of content here.
*/
def("DOMPDF_DEFAULT_MEDIA_TYPE", "screen");
/**
* The default paper size.
*
* North America standard is "letter"; other countries generally "a4"
*
* @see CPDF_Adapter::PAPER_SIZES for valid sizes
*/
def("DOMPDF_DEFAULT_PAPER_SIZE", "letter");
/**
* The default font family
*
* Used if no suitable fonts can be found. This must exist in the font folder.
* @var string
*/
def("DOMPDF_DEFAULT_FONT", "Helvetica");
/**
* Image DPI setting
*
* This setting determines the default DPI setting for images and fonts. The
* DPI may be overridden for inline images by explictly setting the
* image's width & height style attributes (i.e. if the image's native
* width is 600 pixels and you specify the image's width as 72 points,
* the image will have a DPI of 600 in the rendered PDF. The DPI of
* background images can not be overridden and is controlled entirely
* via this parameter.
*
* For the purposes of DOMPDF, pixels per inch (PPI) = dots per inch (DPI).
* If a size in html is given as px (or without unit as image size),
* this tells the corresponding size in pt at 72 DPI.
* This adjusts the relative sizes to be similar to the rendering of the
* html page in a reference browser.
*
* In pdf, always 1 pt = 1/72 inch
*
* Rendering resolution of various browsers in px per inch:
* Windows Firefox and Internet Explorer:
* SystemControl->Display properties->FontResolution: Default:96, largefonts:120, custom:?
* Linux Firefox:
* about:config *resolution: Default:96
* (xorg screen dimension in mm and Desktop font dpi settings are ignored)
*
* Take care about extra font/image zoom factor of browser.
*
* In images, <img> size in pixel attribute, img css style, are overriding
* the real image dimension in px for rendering.
*
* @var int
*/
def("DOMPDF_DPI", 96);
/**
* Enable inline PHP
*
* If this setting is set to true then DOMPDF will automatically evaluate
* inline PHP contained within <script type="text/php"> ... </script> tags.
*
* Enabling this for documents you do not trust (e.g. arbitrary remote html
* pages) is a security risk. Set this option to false if you wish to process
* untrusted documents.
*
* @var bool
*/
def("DOMPDF_ENABLE_PHP", false);
/**
* Enable inline Javascript
*
* If this setting is set to true then DOMPDF will automatically insert
* JavaScript code contained within <script type="text/javascript"> ... </script> tags.
*
* @var bool
*/
def("DOMPDF_ENABLE_JAVASCRIPT", true);
/**
* Enable remote file access
*
* If this setting is set to true, DOMPDF will access remote sites for
* images and CSS files as required.
* This is required for part of test case www/test/image_variants.html through www/examples.php
*
* Attention!
* This can be a security risk, in particular in combination with DOMPDF_ENABLE_PHP and
* allowing remote access to dompdf.php or on allowing remote html code to be passed to
* $dompdf = new DOMPDF(); $dompdf->load_html(...);
* This allows anonymous users to download legally doubtful internet content which on
* tracing back appears to being downloaded by your server, or allows malicious php code
* in remote html pages to be executed by your server with your account privileges.
*
* @var bool
*/
def("DOMPDF_ENABLE_REMOTE", false);
/**
* The debug output log
* @var string
*/
def("DOMPDF_LOG_OUTPUT_FILE", DOMPDF_FONT_DIR."log.htm");
/**
* A ratio applied to the fonts height to be more like browsers' line height
*/
def("DOMPDF_FONT_HEIGHT_RATIO", 1.1);
/**
* Enable CSS float
*
* Allows people to disabled CSS float support
* @var bool
*/
def("DOMPDF_ENABLE_CSS_FLOAT", true);
/**
* Enable the built in DOMPDF autoloader
*
* @var bool
*/
def("DOMPDF_ENABLE_AUTOLOAD", true);
/**
* Prepend the DOMPDF autoload function to the spl_autoload stack
*
* @var bool
*/
def("DOMPDF_AUTOLOAD_PREPEND", false);
/**
* Use the more-than-experimental HTML5 Lib parser
*/
def("DOMPDF_ENABLE_HTML5PARSER", false);
require_once(DOMPDF_LIB_DIR . "/html5lib/Parser.php");
// ### End of user-configurable options ###
/**
* Load autoloader
*/
if (DOMPDF_ENABLE_AUTOLOAD) {
require_once(DOMPDF_INC_DIR . "/autoload.inc.php");
require_once(DOMPDF_LIB_DIR . "/php-font-lib/classes/Font.php");
}
/**
* Ensure that PHP is working with text internally using UTF8 character encoding.
*/
mb_internal_encoding('UTF-8');
/**
* Global array of warnings generated by DomDocument parser and
* stylesheet class
*
* @var array
*/
global $_dompdf_warnings;
$_dompdf_warnings = array();
/**
* If true, $_dompdf_warnings is dumped on script termination when using
* dompdf/dompdf.php or after rendering when using the DOMPDF class.
* When using the class, setting this value to true will prevent you from
* streaming the PDF.
*
* @var bool
*/
global $_dompdf_show_warnings;
$_dompdf_show_warnings = false;
/**
* If true, the entire tree is dumped to stdout in dompdf.cls.php.
* Setting this value to true will prevent you from streaming the PDF.
*
* @var bool
*/
global $_dompdf_debug;
$_dompdf_debug = false;
/**
* Array of enabled debug message types
*
* @var array
*/
global $_DOMPDF_DEBUG_TYPES;
$_DOMPDF_DEBUG_TYPES = array(); //array("page-break" => 1);
/* Optionally enable different classes of debug output before the pdf content.
* Visible if displaying pdf as text,
* E.g. on repeated display of same pdf in browser when pdf is not taken out of
* the browser cache and the premature output prevents setting of the mime type.
*/
def('DEBUGPNG', false);
def('DEBUGKEEPTEMP', false);
def('DEBUGCSS', false);
/* Layout debugging. Will display rectangles around different block levels.
* Visible in the PDF itself.
*/
def('DEBUG_LAYOUT', false);
def('DEBUG_LAYOUT_LINES', false);
def('DEBUG_LAYOUT_BLOCKS', false);
def('DEBUG_LAYOUT_INLINE', false);
def('DEBUG_LAYOUT_PADDINGBOX', false);

View File

@@ -0,0 +1,125 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Positions absolutely positioned frames
*/
class Absolute_Positioner extends Positioner {
function __construct(Frame_Decorator $frame) { parent::__construct($frame); }
function position() {
$frame = $this->_frame;
$style = $frame->get_style();
$p = $frame->find_positionned_parent();
list($x, $y, $w, $h) = $frame->get_containing_block();
$top = $style->length_in_pt($style->top, $h);
$right = $style->length_in_pt($style->right, $w);
$bottom = $style->length_in_pt($style->bottom, $h);
$left = $style->length_in_pt($style->left, $w);
if ( $p && !($left === "auto" && $right === "auto") ) {
// Get the parent's padding box (see http://www.w3.org/TR/CSS21/visuren.html#propdef-top)
list($x, $y, $w, $h) = $p->get_padding_box();
}
list($width, $height) = array($frame->get_margin_width(), $frame->get_margin_height());
$orig_style = $this->_frame->get_original_style();
$orig_width = $orig_style->width;
$orig_height = $orig_style->height;
/****************************
Width auto:
____________| left=auto | left=fixed |
right=auto | A | B |
right=fixed | C | D |
Width fixed:
____________| left=auto | left=fixed |
right=auto | E | F |
right=fixed | G | H |
*****************************/
if ( $left === "auto" ) {
if ( $right === "auto" ) {
// A or E - Keep the frame at the same position
$x = $x + $frame->find_block_parent()->get_current_line_box()->w;
}
else {
if ( $orig_width === "auto" ) {
// C
$x += $w - $width - $right;
}
else {
// G
$x += $w - $width - $right;
}
}
}
else {
if ( $right === "auto" ) {
// B or F
$x += $left;
}
else {
if ( $orig_width === "auto" ) {
// D - TODO change width
$x += $left;
}
else {
// H - Everything is fixed: left + width win
$x += $left;
}
}
}
// The same vertically
if ( $top === "auto" ) {
if ( $bottom === "auto" ) {
// A or E - Keep the frame at the same position
$y = $frame->find_block_parent()->get_current_line_box()->y;
}
else {
if ( $orig_height === "auto" ) {
// C
$y += $h - $height - $bottom;
}
else {
// G
$y += $h - $height - $bottom;
}
}
}
else {
if ( $bottom === "auto" ) {
// B or F
$y += $top;
}
else {
if ( $orig_height === "auto" ) {
// D - TODO change height
$y += $top;
}
else {
// H - Everything is fixed: top + height win
$y += $top;
}
}
}
$frame->set_position($x, $y);
}
}

View File

@@ -0,0 +1,759 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author Helmut Tischer <htischer@weihenstephan.org>
* @author Fabien Ménager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Base renderer class
*
* @access private
* @package dompdf
*/
abstract class Abstract_Renderer {
/**
* Rendering backend
*
* @var Canvas
*/
protected $_canvas;
/**
* Current dompdf instance
*
* @var DOMPDF
*/
protected $_dompdf;
/**
* Class constructor
*
* @param DOMPDF $dompdf The current dompdf instance
*/
function __construct(DOMPDF $dompdf) {
$this->_dompdf = $dompdf;
$this->_canvas = $dompdf->get_canvas();
}
/**
* Render a frame.
*
* Specialized in child classes
*
* @param Frame $frame The frame to render
*/
abstract function render(Frame $frame);
//........................................................................
/**
* Render a background image over a rectangular area
*
* @param string $url The background image to load
* @param float $x The left edge of the rectangular area
* @param float $y The top edge of the rectangular area
* @param float $width The width of the rectangular area
* @param float $height The height of the rectangular area
* @param Style $style The associated Style object
*
* @throws Exception
*/
protected function _background_image($url, $x, $y, $width, $height, $style) {
if ( !function_exists("imagecreatetruecolor") ) {
throw new Exception("The PHP GD extension is required, but is not installed.");
}
$sheet = $style->get_stylesheet();
// Skip degenerate cases
if ( $width == 0 || $height == 0 ) {
return;
}
$box_width = $width;
$box_height = $height;
//debugpng
if (DEBUGPNG) print '[_background_image '.$url.']';
list($img, $type, /*$msg*/) = Image_Cache::resolve_url(
$url,
$sheet->get_protocol(),
$sheet->get_host(),
$sheet->get_base_path(),
$this->_dompdf
);
// Bail if the image is no good
if ( Image_Cache::is_broken($img) ) {
return;
}
//Try to optimize away reading and composing of same background multiple times
//Postponing read with imagecreatefrom ...()
//final composition parameters and name not known yet
//Therefore read dimension directly from file, instead of creating gd object first.
//$img_w = imagesx($src); $img_h = imagesy($src);
list($img_w, $img_h) = dompdf_getimagesize($img);
if (!isset($img_w) || $img_w == 0 || !isset($img_h) || $img_h == 0) {
return;
}
$repeat = $style->background_repeat;
$dpi = $this->_dompdf->get_option("dpi");
//Increase background resolution and dependent box size according to image resolution to be placed in
//Then image can be copied in without resize
$bg_width = round((float)($width * $dpi) / 72);
$bg_height = round((float)($height * $dpi) / 72);
//Need %bg_x, $bg_y as background pos, where img starts, converted to pixel
list($bg_x, $bg_y) = $style->background_position;
if ( is_percent($bg_x) ) {
// The point $bg_x % from the left edge of the image is placed
// $bg_x % from the left edge of the background rectangle
$p = ((float)$bg_x)/100.0;
$x1 = $p * $img_w;
$x2 = $p * $bg_width;
$bg_x = $x2 - $x1;
}
else {
$bg_x = (float)($style->length_in_pt($bg_x)*$dpi) / 72;
}
$bg_x = round($bg_x + $style->length_in_pt($style->border_left_width)*$dpi / 72);
if ( is_percent($bg_y) ) {
// The point $bg_y % from the left edge of the image is placed
// $bg_y % from the left edge of the background rectangle
$p = ((float)$bg_y)/100.0;
$y1 = $p * $img_h;
$y2 = $p * $bg_height;
$bg_y = $y2 - $y1;
}
else {
$bg_y = (float)($style->length_in_pt($bg_y)*$dpi) / 72;
}
$bg_y = round($bg_y + $style->length_in_pt($style->border_top_width)*$dpi / 72);
//clip background to the image area on partial repeat. Nothing to do if img off area
//On repeat, normalize start position to the tile at immediate left/top or 0/0 of area
//On no repeat with positive offset: move size/start to have offset==0
//Handle x/y Dimensions separately
if ( $repeat !== "repeat" && $repeat !== "repeat-x" ) {
//No repeat x
if ($bg_x < 0) {
$bg_width = $img_w + $bg_x;
}
else {
$x += ($bg_x * 72)/$dpi;
$bg_width = $bg_width - $bg_x;
if ($bg_width > $img_w) {
$bg_width = $img_w;
}
$bg_x = 0;
}
if ($bg_width <= 0) {
return;
}
$width = (float)($bg_width * 72)/$dpi;
}
else {
//repeat x
if ($bg_x < 0) {
$bg_x = - ((-$bg_x) % $img_w);
}
else {
$bg_x = $bg_x % $img_w;
if ($bg_x > 0) {
$bg_x -= $img_w;
}
}
}
if ( $repeat !== "repeat" && $repeat !== "repeat-y" ) {
//no repeat y
if ($bg_y < 0) {
$bg_height = $img_h + $bg_y;
}
else {
$y += ($bg_y * 72)/$dpi;
$bg_height = $bg_height - $bg_y;
if ($bg_height > $img_h) {
$bg_height = $img_h;
}
$bg_y = 0;
}
if ($bg_height <= 0) {
return;
}
$height = (float)($bg_height * 72)/$dpi;
}
else {
//repeat y
if ($bg_y < 0) {
$bg_y = - ((-$bg_y) % $img_h);
}
else {
$bg_y = $bg_y % $img_h;
if ($bg_y > 0) {
$bg_y -= $img_h;
}
}
}
//Optimization, if repeat has no effect
if ( $repeat === "repeat" && $bg_y <= 0 && $img_h+$bg_y >= $bg_height ) {
$repeat = "repeat-x";
}
if ( $repeat === "repeat" && $bg_x <= 0 && $img_w+$bg_x >= $bg_width ) {
$repeat = "repeat-y";
}
if ( ($repeat === "repeat-x" && $bg_x <= 0 && $img_w+$bg_x >= $bg_width) ||
($repeat === "repeat-y" && $bg_y <= 0 && $img_h+$bg_y >= $bg_height) ) {
$repeat = "no-repeat";
}
//Use filename as indicator only
//different names for different variants to have different copies in the pdf
//This is not dependent of background color of box! .'_'.(is_array($bg_color) ? $bg_color["hex"] : $bg_color)
//Note: Here, bg_* are the start values, not end values after going through the tile loops!
$filedummy = $img;
$is_png = false;
$filedummy .= '_'.$bg_width.'_'.$bg_height.'_'.$bg_x.'_'.$bg_y.'_'.$repeat;
//Optimization to avoid multiple times rendering the same image.
//If check functions are existing and identical image already cached,
//then skip creation of duplicate, because it is not needed by addImagePng
if ( $this->_canvas instanceof CPDF_Adapter &&
$this->_canvas->get_cpdf()->image_iscached($filedummy) ) {
$bg = null;
}
else {
// Create a new image to fit over the background rectangle
$bg = imagecreatetruecolor($bg_width, $bg_height);
switch (strtolower($type)) {
case IMAGETYPE_PNG:
$is_png = true;
imagesavealpha($bg, true);
imagealphablending($bg, false);
$src = imagecreatefrompng($img);
break;
case IMAGETYPE_JPEG:
$src = imagecreatefromjpeg($img);
break;
case IMAGETYPE_GIF:
$src = imagecreatefromgif($img);
break;
case IMAGETYPE_BMP:
$src = imagecreatefrombmp($img);
break;
default:
return; // Unsupported image type
}
if ( $src == null ) {
return;
}
//Background color if box is not relevant here
//Non transparent image: box clipped to real size. Background non relevant.
//Transparent image: The image controls the transparency and lets shine through whatever background.
//However on transparent image preset the composed image with the transparency color,
//to keep the transparency when copying over the non transparent parts of the tiles.
$ti = imagecolortransparent($src);
if ( $ti >= 0 ) {
$tc = imagecolorsforindex($src, $ti);
$ti = imagecolorallocate($bg, $tc['red'], $tc['green'], $tc['blue']);
imagefill($bg, 0, 0, $ti);
imagecolortransparent($bg, $ti);
}
//This has only an effect for the non repeatable dimension.
//compute start of src and dest coordinates of the single copy
if ( $bg_x < 0 ) {
$dst_x = 0;
$src_x = -$bg_x;
}
else {
$src_x = 0;
$dst_x = $bg_x;
}
if ( $bg_y < 0 ) {
$dst_y = 0;
$src_y = -$bg_y;
}
else {
$src_y = 0;
$dst_y = $bg_y;
}
//For historical reasons exchange meanings of variables:
//start_* will be the start values, while bg_* will be the temporary start values in the loops
$start_x = $bg_x;
$start_y = $bg_y;
// Copy regions from the source image to the background
if ( $repeat === "no-repeat" ) {
// Simply place the image on the background
imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $img_w, $img_h);
}
else if ( $repeat === "repeat-x" ) {
for ( $bg_x = $start_x; $bg_x < $bg_width; $bg_x += $img_w ) {
if ( $bg_x < 0 ) {
$dst_x = 0;
$src_x = -$bg_x;
$w = $img_w + $bg_x;
}
else {
$dst_x = $bg_x;
$src_x = 0;
$w = $img_w;
}
imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $w, $img_h);
}
}
else if ( $repeat === "repeat-y" ) {
for ( $bg_y = $start_y; $bg_y < $bg_height; $bg_y += $img_h ) {
if ( $bg_y < 0 ) {
$dst_y = 0;
$src_y = -$bg_y;
$h = $img_h + $bg_y;
}
else {
$dst_y = $bg_y;
$src_y = 0;
$h = $img_h;
}
imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $img_w, $h);
}
}
else if ( $repeat === "repeat" ) {
for ( $bg_y = $start_y; $bg_y < $bg_height; $bg_y += $img_h ) {
for ( $bg_x = $start_x; $bg_x < $bg_width; $bg_x += $img_w ) {
if ( $bg_x < 0 ) {
$dst_x = 0;
$src_x = -$bg_x;
$w = $img_w + $bg_x;
}
else {
$dst_x = $bg_x;
$src_x = 0;
$w = $img_w;
}
if ( $bg_y < 0 ) {
$dst_y = 0;
$src_y = -$bg_y;
$h = $img_h + $bg_y;
}
else {
$dst_y = $bg_y;
$src_y = 0;
$h = $img_h;
}
imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $w, $h);
}
}
}
else {
print 'Unknown repeat!';
}
imagedestroy($src);
} /* End optimize away creation of duplicates */
$this->_canvas->clipping_rectangle($x, $y, $box_width, $box_height);
//img: image url string
//img_w, img_h: original image size in px
//width, height: box size in pt
//bg_width, bg_height: box size in px
//x, y: left/top edge of box on page in pt
//start_x, start_y: placement of image relative to pattern
//$repeat: repeat mode
//$bg: GD object of result image
//$src: GD object of original image
//When using cpdf and optimization to direct png creation from gd object is available,
//don't create temp file, but place gd object directly into the pdf
if ( !$is_png && $this->_canvas instanceof CPDF_Adapter ) {
// Note: CPDF_Adapter image converts y position
$this->_canvas->get_cpdf()->addImagePng($filedummy, $x, $this->_canvas->get_height() - $y - $height, $width, $height, $bg);
}
else {
$tmp_dir = $this->_dompdf->get_option("temp_dir");
$tmp_name = tempnam($tmp_dir, "bg_dompdf_img_");
@unlink($tmp_name);
$tmp_file = "$tmp_name.png";
//debugpng
if (DEBUGPNG) print '[_background_image '.$tmp_file.']';
imagepng($bg, $tmp_file);
$this->_canvas->image($tmp_file, $x, $y, $width, $height);
imagedestroy($bg);
//debugpng
if (DEBUGPNG) print '[_background_image unlink '.$tmp_file.']';
if (!DEBUGKEEPTEMP) {
unlink($tmp_file);
}
}
$this->_canvas->clipping_end();
}
protected function _get_dash_pattern($style, $width) {
$pattern = array();
switch ($style) {
default:
/*case "solid":
case "double":
case "groove":
case "inset":
case "outset":
case "ridge":*/
case "none": break;
case "dotted":
if ( $width <= 1 )
$pattern = array($width, $width*2);
else
$pattern = array($width);
break;
case "dashed":
$pattern = array(3 * $width);
break;
}
return $pattern;
}
protected function _border_none($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
return;
}
protected function _border_hidden($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
return;
}
// Border rendering functions
protected function _border_dotted($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
$this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "dotted", $r1, $r2);
}
protected function _border_dashed($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
$this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "dashed", $r1, $r2);
}
protected function _border_solid($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
// TODO: Solve rendering where one corner is beveled (radius == 0), one corner isn't.
if ( $corner_style !== "bevel" || $r1 > 0 || $r2 > 0 ) {
// do it the simple way
$this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "solid", $r1, $r2);
return;
}
list($top, $right, $bottom, $left) = $widths;
// All this polygon business is for beveled corners...
switch ($side) {
case "top":
$points = array($x, $y,
$x + $length, $y,
$x + $length - $right, $y + $top,
$x + $left, $y + $top);
$this->_canvas->polygon($points, $color, null, null, true);
break;
case "bottom":
$points = array($x, $y,
$x + $length, $y,
$x + $length - $right, $y - $bottom,
$x + $left, $y - $bottom);
$this->_canvas->polygon($points, $color, null, null, true);
break;
case "left":
$points = array($x, $y,
$x, $y + $length,
$x + $left, $y + $length - $bottom,
$x + $left, $y + $top);
$this->_canvas->polygon($points, $color, null, null, true);
break;
case "right":
$points = array($x, $y,
$x, $y + $length,
$x - $right, $y + $length - $bottom,
$x - $right, $y + $top);
$this->_canvas->polygon($points, $color, null, null, true);
break;
default:
return;
}
}
protected function _apply_ratio($side, $ratio, $top, $right, $bottom, $left, &$x, &$y, &$length, &$r1, &$r2) {
switch ($side) {
case "top":
$r1 -= $left * $ratio;
$r2 -= $right * $ratio;
$x += $left * $ratio;
$y += $top * $ratio;
$length -= $left * $ratio + $right * $ratio;
break;
case "bottom":
$r1 -= $right * $ratio;
$r2 -= $left * $ratio;
$x += $left * $ratio;
$y -= $bottom * $ratio;
$length -= $left * $ratio + $right * $ratio;
break;
case "left":
$r1 -= $top * $ratio;
$r2 -= $bottom * $ratio;
$x += $left * $ratio;
$y += $top * $ratio;
$length -= $top * $ratio + $bottom * $ratio;
break;
case "right":
$r1 -= $bottom * $ratio;
$r2 -= $top * $ratio;
$x -= $right * $ratio;
$y += $top * $ratio;
$length -= $top * $ratio + $bottom * $ratio;
break;
default:
return;
}
}
protected function _border_double($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
list($top, $right, $bottom, $left) = $widths;
$third_widths = array($top / 3, $right / 3, $bottom / 3, $left / 3);
// draw the outer border
$this->_border_solid($x, $y, $length, $color, $third_widths, $side, $corner_style, $r1, $r2);
$this->_apply_ratio($side, 2/3, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2);
$this->_border_solid($x, $y, $length, $color, $third_widths, $side, $corner_style, $r1, $r2);
}
protected function _border_groove($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
list($top, $right, $bottom, $left) = $widths;
$half_widths = array($top / 2, $right / 2, $bottom / 2, $left / 2);
$this->_border_inset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2);
$this->_apply_ratio($side, 0.5, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2);
$this->_border_outset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2);
}
protected function _border_ridge($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
list($top, $right, $bottom, $left) = $widths;
$half_widths = array($top / 2, $right / 2, $bottom / 2, $left / 2);
$this->_border_outset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2);
$this->_apply_ratio($side, 0.5, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2);
$this->_border_inset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2);
}
protected function _tint($c) {
if ( !is_numeric($c) )
return $c;
return min(1, $c + 0.16);
}
protected function _shade($c) {
if ( !is_numeric($c) )
return $c;
return max(0, $c - 0.33);
}
protected function _border_inset($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
switch ($side) {
case "top":
case "left":
$shade = array_map(array($this, "_shade"), $color);
$this->_border_solid($x, $y, $length, $shade, $widths, $side, $corner_style, $r1, $r2);
break;
case "bottom":
case "right":
$tint = array_map(array($this, "_tint"), $color);
$this->_border_solid($x, $y, $length, $tint, $widths, $side, $corner_style, $r1, $r2);
break;
default:
return;
}
}
protected function _border_outset($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
switch ($side) {
case "top":
case "left":
$tint = array_map(array($this, "_tint"), $color);
$this->_border_solid($x, $y, $length, $tint, $widths, $side, $corner_style, $r1, $r2);
break;
case "bottom":
case "right":
$shade = array_map(array($this, "_shade"), $color);
$this->_border_solid($x, $y, $length, $shade, $widths, $side, $corner_style, $r1, $r2);
break;
default:
return;
}
}
// Draws a solid, dotted, or dashed line, observing the border radius
protected function _border_line($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $pattern_name, $r1 = 0, $r2 = 0) {
list($top, $right, $bottom, $left) = $widths;
$width = $$side;
$pattern = $this->_get_dash_pattern($pattern_name, $width);
$half_width = $width/2;
$r1 -= $half_width;
$r2 -= $half_width;
$adjust = $r1/80;
$length -= $width;
switch ($side) {
case "top":
$x += $half_width;
$y += $half_width;
if ( $r1 > 0 ) {
$this->_canvas->arc($x + $r1, $y + $r1, $r1, $r1, 90-$adjust, 135+$adjust, $color, $width, $pattern);
}
$this->_canvas->line($x + $r1, $y, $x + $length - $r2, $y, $color, $width, $pattern);
if ( $r2 > 0 ) {
$this->_canvas->arc($x + $length - $r2, $y + $r2, $r2, $r2, 45-$adjust, 90+$adjust, $color, $width, $pattern);
}
break;
case "bottom":
$x += $half_width;
$y -= $half_width;
if ( $r1 > 0 ) {
$this->_canvas->arc($x + $r1, $y - $r1, $r1, $r1, 225-$adjust, 270+$adjust, $color, $width, $pattern);
}
$this->_canvas->line($x + $r1, $y, $x + $length - $r2, $y, $color, $width, $pattern);
if ( $r2 > 0 ) {
$this->_canvas->arc($x + $length - $r2, $y - $r2, $r2, $r2, 270-$adjust, 315+$adjust, $color, $width, $pattern);
}
break;
case "left":
$y += $half_width;
$x += $half_width;
if ( $r1 > 0 ) {
$this->_canvas->arc($x + $r1, $y + $r1, $r1, $r1, 135-$adjust, 180+$adjust, $color, $width, $pattern);
}
$this->_canvas->line($x, $y + $r1, $x, $y + $length - $r2, $color, $width, $pattern);
if ( $r2 > 0 ) {
$this->_canvas->arc($x + $r2, $y + $length - $r2, $r2, $r2, 180-$adjust, 225+$adjust, $color, $width, $pattern);
}
break;
case "right":
$y += $half_width;
$x -= $half_width;
if ( $r1 > 0 ) {
$this->_canvas->arc($x - $r1, $y + $r1, $r1, $r1, 0-$adjust, 45+$adjust, $color, $width, $pattern);
}
$this->_canvas->line($x, $y + $r1, $x, $y + $length - $r2, $color, $width, $pattern);
if ( $r2 > 0 ) {
$this->_canvas->arc($x - $r2, $y + $length - $r2, $r2, $r2, 315-$adjust, 360+$adjust, $color, $width, $pattern);
}
break;
}
}
protected function _set_opacity($opacity) {
if ( is_numeric($opacity) && $opacity <= 1.0 && $opacity >= 0.0 ) {
$this->_canvas->set_opacity( $opacity );
}
}
protected function _debug_layout($box, $color = "red", $style = array()) {
$this->_canvas->rectangle($box[0], $box[1], $box[2], $box[3], CSS_Color::parse($color), 0.1, $style);
}
}

View File

@@ -0,0 +1,592 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author Fabien Ménager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Translates HTML 4.0 attributes into CSS rules
*
* @package dompdf
*/
class Attribute_Translator {
static $_style_attr = "_html_style_attribute";
// Munged data originally from
// http://www.w3.org/TR/REC-html40/index/attributes.html
// http://www.cs.tut.fi/~jkorpela/html2css.html
static private $__ATTRIBUTE_LOOKUP = array(
//'caption' => array ( 'align' => '', ),
'img' => array(
'align' => array(
'bottom' => 'vertical-align: baseline;',
'middle' => 'vertical-align: middle;',
'top' => 'vertical-align: top;',
'left' => 'float: left;',
'right' => 'float: right;'
),
'border' => 'border: %0.2F px solid;',
'height' => 'height: %s px;',
'hspace' => 'padding-left: %1$0.2F px; padding-right: %1$0.2F px;',
'vspace' => 'padding-top: %1$0.2F px; padding-bottom: %1$0.2F px;',
'width' => 'width: %s px;',
),
'table' => array(
'align' => array(
'left' => 'margin-left: 0; margin-right: auto;',
'center' => 'margin-left: auto; margin-right: auto;',
'right' => 'margin-left: auto; margin-right: 0;'
),
'bgcolor' => 'background-color: %s;',
'border' => '!set_table_border',
'cellpadding' => '!set_table_cellpadding',//'border-spacing: %0.2F; border-collapse: separate;',
'cellspacing' => '!set_table_cellspacing',
'frame' => array(
'void' => 'border-style: none;',
'above' => 'border-top-style: solid;',
'below' => 'border-bottom-style: solid;',
'hsides' => 'border-left-style: solid; border-right-style: solid;',
'vsides' => 'border-top-style: solid; border-bottom-style: solid;',
'lhs' => 'border-left-style: solid;',
'rhs' => 'border-right-style: solid;',
'box' => 'border-style: solid;',
'border' => 'border-style: solid;'
),
'rules' => '!set_table_rules',
'width' => 'width: %s;',
),
'hr' => array(
'align' => '!set_hr_align', // Need to grab width to set 'left' & 'right' correctly
'noshade' => 'border-style: solid;',
'size' => '!set_hr_size', //'border-width: %0.2F px;',
'width' => 'width: %s;',
),
'div' => array(
'align' => 'text-align: %s;',
),
'h1' => array(
'align' => 'text-align: %s;',
),
'h2' => array(
'align' => 'text-align: %s;',
),
'h3' => array(
'align' => 'text-align: %s;',
),
'h4' => array(
'align' => 'text-align: %s;',
),
'h5' => array(
'align' => 'text-align: %s;',
),
'h6' => array(
'align' => 'text-align: %s;',
),
'p' => array(
'align' => 'text-align: %s;',
),
// 'col' => array(
// 'align' => '',
// 'valign' => '',
// ),
// 'colgroup' => array(
// 'align' => '',
// 'valign' => '',
// ),
'tbody' => array(
'align' => '!set_table_row_align',
'valign' => '!set_table_row_valign',
),
'td' => array(
'align' => 'text-align: %s;',
'bgcolor' => '!set_background_color',
'height' => 'height: %s;',
'nowrap' => 'white-space: nowrap;',
'valign' => 'vertical-align: %s;',
'width' => 'width: %s;',
),
'tfoot' => array(
'align' => '!set_table_row_align',
'valign' => '!set_table_row_valign',
),
'th' => array(
'align' => 'text-align: %s;',
'bgcolor' => '!set_background_color',
'height' => 'height: %s;',
'nowrap' => 'white-space: nowrap;',
'valign' => 'vertical-align: %s;',
'width' => 'width: %s;',
),
'thead' => array(
'align' => '!set_table_row_align',
'valign' => '!set_table_row_valign',
),
'tr' => array(
'align' => '!set_table_row_align',
'bgcolor' => '!set_table_row_bgcolor',
'valign' => '!set_table_row_valign',
),
'body' => array(
'background' => 'background-image: url(%s);',
'bgcolor' => '!set_background_color',
'link' => '!set_body_link',
'text' => '!set_color',
),
'br' => array(
'clear' => 'clear: %s;',
),
'basefont' => array(
'color' => '!set_color',
'face' => 'font-family: %s;',
'size' => '!set_basefont_size',
),
'font' => array(
'color' => '!set_color',
'face' => 'font-family: %s;',
'size' => '!set_font_size',
),
'dir' => array(
'compact' => 'margin: 0.5em 0;',
),
'dl' => array(
'compact' => 'margin: 0.5em 0;',
),
'menu' => array(
'compact' => 'margin: 0.5em 0;',
),
'ol' => array(
'compact' => 'margin: 0.5em 0;',
'start' => 'counter-reset: -dompdf-default-counter %d;',
'type' => 'list-style-type: %s;',
),
'ul' => array(
'compact' => 'margin: 0.5em 0;',
'type' => 'list-style-type: %s;',
),
'li' => array(
'type' => 'list-style-type: %s;',
'value' => 'counter-reset: -dompdf-default-counter %d;',
),
'pre' => array(
'width' => 'width: %s;',
),
);
static protected $_last_basefont_size = 3;
static protected $_font_size_lookup = array(
// For basefont support
-3 => "4pt",
-2 => "5pt",
-1 => "6pt",
0 => "7pt",
1 => "8pt",
2 => "10pt",
3 => "12pt",
4 => "14pt",
5 => "18pt",
6 => "24pt",
7 => "34pt",
// For basefont support
8 => "48pt",
9 => "44pt",
10 => "52pt",
11 => "60pt",
);
/**
* @param Frame $frame
*/
static function translate_attributes(Frame $frame) {
$node = $frame->get_node();
$tag = $node->nodeName;
if ( !isset(self::$__ATTRIBUTE_LOOKUP[$tag]) ) {
return;
}
$valid_attrs = self::$__ATTRIBUTE_LOOKUP[$tag];
$attrs = $node->attributes;
$style = rtrim($node->getAttribute(self::$_style_attr), "; ");
if ( $style != "" ) {
$style .= ";";
}
foreach ($attrs as $attr => $attr_node ) {
if ( !isset($valid_attrs[$attr]) ) {
continue;
}
$value = $attr_node->value;
$target = $valid_attrs[$attr];
// Look up $value in $target, if $target is an array:
if ( is_array($target) ) {
if ( isset($target[$value]) ) {
$style .= " " . self::_resolve_target($node, $target[$value], $value);
}
}
else {
// otherwise use target directly
$style .= " " . self::_resolve_target($node, $target, $value);
}
}
if ( !is_null($style) ) {
$style = ltrim($style);
$node->setAttribute(self::$_style_attr, $style);
}
}
/**
* @param DOMNode $node
* @param string $target
* @param string $value
*
* @return string
*/
static protected function _resolve_target(DOMNode $node, $target, $value) {
if ( $target[0] === "!" ) {
// Function call
$func = "_" . mb_substr($target, 1);
return self::$func($node, $value);
}
return $value ? sprintf($target, $value) : "";
}
/**
* @param DOMElement $node
* @param string $new_style
*/
static function append_style(DOMElement $node, $new_style) {
$style = rtrim($node->getAttribute(self::$_style_attr), ";");
$style .= $new_style;
$style = ltrim($style, ";");
$node->setAttribute(self::$_style_attr, $style);
}
/**
* @param DOMNode $node
*
* @return DOMNodeList|DOMElement[]
*/
static protected function get_cell_list(DOMNode $node) {
$xpath = new DOMXpath($node->ownerDocument);
switch($node->nodeName) {
default:
case "table":
$query = "tr/td | thead/tr/td | tbody/tr/td | tfoot/tr/td | tr/th | thead/tr/th | tbody/tr/th | tfoot/tr/th";
break;
case "tbody":
case "tfoot":
case "thead":
$query = "tr/td | tr/th";
break;
case "tr":
$query = "td | th";
break;
}
return $xpath->query($query, $node);
}
/**
* @param string $value
*
* @return string
*/
static protected function _get_valid_color($value) {
if ( preg_match('/^#?([0-9A-F]{6})$/i', $value, $matches) ) {
$value = "#$matches[1]";
}
return $value;
}
/**
* @param DOMElement $node
* @param string $value
*
* @return string
*/
static protected function _set_color(DOMElement $node, $value) {
$value = self::_get_valid_color($value);
return "color: $value;";
}
/**
* @param DOMElement $node
* @param string $value
*
* @return string
*/
static protected function _set_background_color(DOMElement $node, $value) {
$value = self::_get_valid_color($value);
return "background-color: $value;";
}
/**
* @param DOMElement $node
* @param string $value
*
* @return null
*/
static protected function _set_table_cellpadding(DOMElement $node, $value) {
$cell_list = self::get_cell_list($node);
foreach ($cell_list as $cell) {
self::append_style($cell, "; padding: {$value}px;");
}
return null;
}
/**
* @param DOMElement $node
* @param string $value
*
* @return string
*/
static protected function _set_table_border(DOMElement $node, $value) {
$cell_list = self::get_cell_list($node);
foreach ($cell_list as $cell) {
$style = rtrim($cell->getAttribute(self::$_style_attr));
$style .= "; border-width: " . ($value > 0 ? 1 : 0) . "pt; border-style: inset;";
$style = ltrim($style, ";");
$cell->setAttribute(self::$_style_attr, $style);
}
$style = rtrim($node->getAttribute(self::$_style_attr), ";");
$style .= "; border-width: $value" . "px; ";
return ltrim($style, "; ");
}
/**
* @param DOMElement $node
* @param string $value
*
* @return string
*/
static protected function _set_table_cellspacing(DOMElement $node, $value) {
$style = rtrim($node->getAttribute(self::$_style_attr), ";");
if ( $value == 0 ) {
$style .= "; border-collapse: collapse;";
}
else {
$style .= "; border-spacing: {$value}px; border-collapse: separate;";
}
return ltrim($style, ";");
}
/**
* @param DOMElement $node
* @param string $value
*
* @return null|string
*/
static protected function _set_table_rules(DOMElement $node, $value) {
$new_style = "; border-collapse: collapse;";
switch ($value) {
case "none":
$new_style .= "border-style: none;";
break;
case "groups":
// FIXME: unsupported
return null;
case "rows":
$new_style .= "border-style: solid none solid none; border-width: 1px; ";
break;
case "cols":
$new_style .= "border-style: none solid none solid; border-width: 1px; ";
break;
case "all":
$new_style .= "border-style: solid; border-width: 1px; ";
break;
default:
// Invalid value
return null;
}
$cell_list = self::get_cell_list($node);
foreach ($cell_list as $cell) {
$style = $cell->getAttribute(self::$_style_attr);
$style .= $new_style;
$cell->setAttribute(self::$_style_attr, $style);
}
$style = rtrim($node->getAttribute(self::$_style_attr), ";");
$style .= "; border-collapse: collapse; ";
return ltrim($style, "; ");
}
/**
* @param DOMElement $node
* @param string $value
*
* @return string
*/
static protected function _set_hr_size(DOMElement $node, $value) {
$style = rtrim($node->getAttribute(self::$_style_attr), ";");
$style .= "; border-width: ".max(0, $value-2)."; ";
return ltrim($style, "; ");
}
/**
* @param DOMElement $node
* @param string $value
*
* @return null|string
*/
static protected function _set_hr_align(DOMElement $node, $value) {
$style = rtrim($node->getAttribute(self::$_style_attr),";");
$width = $node->getAttribute("width");
if ( $width == "" ) {
$width = "100%";
}
$remainder = 100 - (double)rtrim($width, "% ");
switch ($value) {
case "left":
$style .= "; margin-right: $remainder %;";
break;
case "right":
$style .= "; margin-left: $remainder %;";
break;
case "center":
$style .= "; margin-left: auto; margin-right: auto;";
break;
default:
return null;
}
return ltrim($style, "; ");
}
/**
* @param DOMElement $node
* @param string $value
*
* @return null
*/
static protected function _set_table_row_align(DOMElement $node, $value) {
$cell_list = self::get_cell_list($node);
foreach ($cell_list as $cell) {
self::append_style($cell, "; text-align: $value;");
}
return null;
}
/**
* @param DOMElement $node
* @param string $value
*
* @return null
*/
static protected function _set_table_row_valign(DOMElement $node, $value) {
$cell_list = self::get_cell_list($node);
foreach ($cell_list as $cell) {
self::append_style($cell, "; vertical-align: $value;");
}
return null;
}
/**
* @param DOMElement $node
* @param string $value
*
* @return null
*/
static protected function _set_table_row_bgcolor(DOMElement $node, $value) {
$cell_list = self::get_cell_list($node);
$value = self::_get_valid_color($value);
foreach ($cell_list as $cell) {
self::append_style($cell, "; background-color: $value;");
}
return null;
}
/**
* @param DOMElement $node
* @param string $value
*
* @return null
*/
static protected function _set_body_link(DOMElement $node, $value) {
$a_list = $node->getElementsByTagName("a");
$value = self::_get_valid_color($value);
foreach ($a_list as $a) {
self::append_style($a, "; color: $value;");
}
return null;
}
/**
* @param DOMElement $node
* @param string $value
*
* @return null
*/
static protected function _set_basefont_size(DOMElement $node, $value) {
// FIXME: ? we don't actually set the font size of anything here, just
// the base size for later modification by <font> tags.
self::$_last_basefont_size = $value;
return null;
}
/**
* @param DOMElement $node
* @param string $value
*
* @return string
*/
static protected function _set_font_size(DOMElement $node, $value) {
$style = $node->getAttribute(self::$_style_attr);
if ( $value[0] === "-" || $value[0] === "+" ) {
$value = self::$_last_basefont_size + (int)$value;
}
if ( isset(self::$_font_size_lookup[$value]) ) {
$style .= "; font-size: " . self::$_font_size_lookup[$value] . ";";
}
else {
$style .= "; font-size: $value;";
}
return ltrim($style, "; ");
}
}

View File

@@ -0,0 +1,86 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author Fabien Ménager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* DOMPDF autoload function
*
* If you have an existing autoload function, add a call to this function
* from your existing __autoload() implementation.
*
* @param string $class
*/
function DOMPDF_autoload($class) {
$filename = DOMPDF_INC_DIR . "/" . mb_strtolower($class) . ".cls.php";
if ( is_file($filename) ) {
include_once $filename;
}
}
// If SPL autoload functions are available (PHP >= 5.1.2)
if ( function_exists("spl_autoload_register") ) {
$autoload = "DOMPDF_autoload";
$funcs = spl_autoload_functions();
// No functions currently in the stack.
if ( !DOMPDF_AUTOLOAD_PREPEND || $funcs === false ) {
spl_autoload_register($autoload);
}
// If PHP >= 5.3 the $prepend argument is available
else if ( PHP_VERSION_ID >= 50300 ) {
spl_autoload_register($autoload, true, true);
}
else {
// Unregister existing autoloaders...
$compat = (PHP_VERSION_ID <= 50102 && PHP_VERSION_ID >= 50100);
foreach ($funcs as $func) {
if (is_array($func)) {
// :TRICKY: There are some compatibility issues and some
// places where we need to error out
$reflector = new ReflectionMethod($func[0], $func[1]);
if (!$reflector->isStatic()) {
throw new Exception('This function is not compatible with non-static object methods due to PHP Bug #44144.');
}
// Suprisingly, spl_autoload_register supports the
// Class::staticMethod callback format, although call_user_func doesn't
if ($compat) $func = implode('::', $func);
}
spl_autoload_unregister($func);
}
// Register the new one, thus putting it at the front of the stack...
spl_autoload_register($autoload);
// Now, go back and re-register all of our old ones.
foreach ($funcs as $func) {
spl_autoload_register($func);
}
// Be polite and ensure that userland autoload gets retained
if ( function_exists("__autoload") ) {
spl_autoload_register("__autoload");
}
}
}
else if ( !function_exists("__autoload") ) {
/**
* Default __autoload() function
*
* @param string $class
*/
function __autoload($class) {
DOMPDF_autoload($class);
}
}

View File

@@ -0,0 +1,234 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Decorates frames for block layout
*
* @access private
* @package dompdf
*/
class Block_Frame_Decorator extends Frame_Decorator {
/**
* Current line index
*
* @var int
*/
protected $_cl;
/**
* The block's line boxes
*
* @var Line_Box[]
*/
protected $_line_boxes;
function __construct(Frame $frame, DOMPDF $dompdf) {
parent::__construct($frame, $dompdf);
$this->_line_boxes = array(new Line_Box($this));
$this->_cl = 0;
}
function reset() {
parent::reset();
$this->_line_boxes = array(new Line_Box($this));
$this->_cl = 0;
}
/**
* @return Line_Box
*/
function get_current_line_box() {
return $this->_line_boxes[$this->_cl];
}
/**
* @return integer
*/
function get_current_line_number() {
return $this->_cl;
}
/**
* @return Line_Box[]
*/
function get_line_boxes() {
return $this->_line_boxes;
}
/**
* @param integer $i
*/
function clear_line($i) {
if ( isset($this->_line_boxes[$i]) ) {
unset($this->_line_boxes[$i]);
}
}
/**
* @param Frame $frame
*/
function add_frame_to_line(Frame $frame) {
if ( !$frame->is_in_flow() ) {
return;
}
$style = $frame->get_style();
$frame->set_containing_line($this->_line_boxes[$this->_cl]);
/*
// Adds a new line after a block, only if certain conditions are met
if ((($frame instanceof Inline_Frame_Decorator && $frame->get_node()->nodeName !== "br") ||
$frame instanceof Text_Frame_Decorator && trim($frame->get_text())) &&
($frame->get_prev_sibling() && $frame->get_prev_sibling()->get_style()->display === "block" &&
$this->_line_boxes[$this->_cl]->w > 0 )) {
$this->maximize_line_height( $style->length_in_pt($style->line_height), $frame );
$this->add_line();
// Add each child of the inline frame to the line individually
foreach ($frame->get_children() as $child)
$this->add_frame_to_line( $child );
}
else*/
// Handle inline frames (which are effectively wrappers)
if ( $frame instanceof Inline_Frame_Decorator ) {
// Handle line breaks
if ( $frame->get_node()->nodeName === "br" ) {
$this->maximize_line_height( $style->length_in_pt($style->line_height), $frame );
$this->add_line(true);
}
return;
}
// Trim leading text if this is an empty line. Kinda a hack to put it here,
// but what can you do...
if ( $this->get_current_line_box()->w == 0 &&
$frame->is_text_node() &&
!$frame->is_pre() ) {
$frame->set_text( ltrim($frame->get_text()) );
$frame->recalculate_width();
}
$w = $frame->get_margin_width();
if ( $w == 0 ) {
return;
}
// Debugging code:
/*
pre_r("\n<h3>Adding frame to line:</h3>");
// pre_r("Me: " . $this->get_node()->nodeName . " (" . spl_object_hash($this->get_node()) . ")");
// pre_r("Node: " . $frame->get_node()->nodeName . " (" . spl_object_hash($frame->get_node()) . ")");
if ( $frame->is_text_node() )
pre_r('"'.$frame->get_node()->nodeValue.'"');
pre_r("Line width: " . $this->_line_boxes[$this->_cl]->w);
pre_r("Frame: " . get_class($frame));
pre_r("Frame width: " . $w);
pre_r("Frame height: " . $frame->get_margin_height());
pre_r("Containing block width: " . $this->get_containing_block("w"));
*/
// End debugging
$line = $this->_line_boxes[$this->_cl];
if ( $line->left + $line->w + $line->right + $w > $this->get_containing_block("w")) {
$this->add_line();
}
$frame->position();
$current_line = $this->_line_boxes[$this->_cl];
$current_line->add_frame($frame);
if ( $frame->is_text_node() ) {
$current_line->wc += count(preg_split("/\s+/", trim($frame->get_text())));
}
$this->increase_line_width($w);
$this->maximize_line_height($frame->get_margin_height(), $frame);
}
function remove_frames_from_line(Frame $frame) {
// Search backwards through the lines for $frame
$i = $this->_cl;
$j = null;
while ($i >= 0) {
if ( ($j = in_array($frame, $this->_line_boxes[$i]->get_frames(), true)) !== false ) {
break;
}
$i--;
}
if ( $j === false ) {
return;
}
// Remove $frame and all frames that follow
while ($j < count($this->_line_boxes[$i]->get_frames())) {
$frames = $this->_line_boxes[$i]->get_frames();
$f = $frames[$j];
$frames[$j] = null;
unset($frames[$j]);
$j++;
$this->_line_boxes[$i]->w -= $f->get_margin_width();
}
// Recalculate the height of the line
$h = 0;
foreach ($this->_line_boxes[$i]->get_frames() as $f) {
$h = max( $h, $f->get_margin_height() );
}
$this->_line_boxes[$i]->h = $h;
// Remove all lines that follow
while ($this->_cl > $i) {
$this->_line_boxes[ $this->_cl ] = null;
unset($this->_line_boxes[ $this->_cl ]);
$this->_cl--;
}
}
function increase_line_width($w) {
$this->_line_boxes[ $this->_cl ]->w += $w;
}
function maximize_line_height($val, Frame $frame) {
if ( $val > $this->_line_boxes[ $this->_cl ]->h ) {
$this->_line_boxes[ $this->_cl ]->tallest_frame = $frame;
$this->_line_boxes[ $this->_cl ]->h = $val;
}
}
function add_line($br = false) {
// if ( $this->_line_boxes[$this->_cl]["h"] == 0 ) //count($this->_line_boxes[$i]["frames"]) == 0 ||
// return;
$this->_line_boxes[$this->_cl]->br = $br;
$y = $this->_line_boxes[$this->_cl]->y + $this->_line_boxes[$this->_cl]->h;
$new_line = new Line_Box($this, $y);
$this->_line_boxes[ ++$this->_cl ] = $new_line;
}
//........................................................................
}

View File

@@ -0,0 +1,805 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author Fabien Ménager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Reflows block frames
*
* @access private
* @package dompdf
*/
class Block_Frame_Reflower extends Frame_Reflower {
// Minimum line width to justify, as fraction of available width
const MIN_JUSTIFY_WIDTH = 0.80;
/**
* @var Block_Frame_Decorator
*/
protected $_frame;
function __construct(Block_Frame_Decorator $frame) { parent::__construct($frame); }
/**
* Calculate the ideal used value for the width property as per:
* http://www.w3.org/TR/CSS21/visudet.html#Computing_widths_and_margins
*
* @param float $width
* @return array
*/
protected function _calculate_width($width) {
$frame = $this->_frame;
$style = $frame->get_style();
$w = $frame->get_containing_block("w");
if ( $style->position === "fixed" ) {
$w = $frame->get_parent()->get_containing_block("w");
}
$rm = $style->length_in_pt($style->margin_right, $w);
$lm = $style->length_in_pt($style->margin_left, $w);
$left = $style->length_in_pt($style->left, $w);
$right = $style->length_in_pt($style->right, $w);
// Handle 'auto' values
$dims = array($style->border_left_width,
$style->border_right_width,
$style->padding_left,
$style->padding_right,
$width !== "auto" ? $width : 0,
$rm !== "auto" ? $rm : 0,
$lm !== "auto" ? $lm : 0);
// absolutely positioned boxes take the 'left' and 'right' properties into account
if ( $frame->is_absolute() ) {
$absolute = true;
$dims[] = $left !== "auto" ? $left : 0;
$dims[] = $right !== "auto" ? $right : 0;
}
else {
$absolute = false;
}
$sum = $style->length_in_pt($dims, $w);
// Compare to the containing block
$diff = $w - $sum;
if ( $diff > 0 ) {
if ( $absolute ) {
// resolve auto properties: see
// http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width
if ( $width === "auto" && $left === "auto" && $right === "auto" ) {
if ( $lm === "auto" ) $lm = 0;
if ( $rm === "auto" ) $rm = 0;
// Technically, the width should be "shrink-to-fit" i.e. based on the
// preferred width of the content... a little too costly here as a
// special case. Just get the width to take up the slack:
$left = 0;
$right = 0;
$width = $diff;
}
else if ( $width === "auto" ) {
if ( $lm === "auto" ) $lm = 0;
if ( $rm === "auto" ) $rm = 0;
if ( $left === "auto" ) $left = 0;
if ( $right === "auto" ) $right = 0;
$width = $diff;
}
else if ( $left === "auto" ) {
if ( $lm === "auto" ) $lm = 0;
if ( $rm === "auto" ) $rm = 0;
if ( $right === "auto" ) $right = 0;
$left = $diff;
}
else if ( $right === "auto" ) {
if ( $lm === "auto" ) $lm = 0;
if ( $rm === "auto" ) $rm = 0;
$right = $diff;
}
}
else {
// Find auto properties and get them to take up the slack
if ( $width === "auto" ) {
$width = $diff;
}
else if ( $lm === "auto" && $rm === "auto" ) {
$lm = $rm = round($diff / 2);
}
else if ( $lm === "auto" ) {
$lm = $diff;
}
else if ( $rm === "auto" ) {
$rm = $diff;
}
}
}
else if ($diff < 0) {
// We are over constrained--set margin-right to the difference
$rm = $diff;
}
return array(
"width" => $width,
"margin_left" => $lm,
"margin_right" => $rm,
"left" => $left,
"right" => $right,
);
}
/**
* Call the above function, but resolve max/min widths
*
* @throws DOMPDF_Exception
* @return array
*/
protected function _calculate_restricted_width() {
$frame = $this->_frame;
$style = $frame->get_style();
$cb = $frame->get_containing_block();
if ( $style->position === "fixed" ) {
$cb = $frame->get_root()->get_containing_block();
}
//if ( $style->position === "absolute" )
// $cb = $frame->find_positionned_parent()->get_containing_block();
if ( !isset($cb["w"]) ) {
throw new DOMPDF_Exception("Box property calculation requires containing block width");
}
// Treat width 100% as auto
if ( $style->width === "100%" ) {
$width = "auto";
}
else {
$width = $style->length_in_pt($style->width, $cb["w"]);
}
extract($this->_calculate_width($width));
// Handle min/max width
$min_width = $style->length_in_pt($style->min_width, $cb["w"]);
$max_width = $style->length_in_pt($style->max_width, $cb["w"]);
if ( $max_width !== "none" && $min_width > $max_width ) {
list($max_width, $min_width) = array($min_width, $max_width);
}
if ( $max_width !== "none" && $width > $max_width ) {
extract($this->_calculate_width($max_width));
}
if ( $width < $min_width ) {
extract($this->_calculate_width($min_width));
}
return array($width, $margin_left, $margin_right, $left, $right);
}
/**
* Determine the unrestricted height of content within the block
* not by adding each line's height, but by getting the last line's position.
* This because lines could have been pushed lower by a clearing element.
*
* @return float
*/
protected function _calculate_content_height() {
$lines = $this->_frame->get_line_boxes();
$height = 0;
foreach ($lines as $line) {
$height += $line->h;
}
/*
$first_line = reset($lines);
$last_line = end($lines);
$height2 = $last_line->y + $last_line->h - $first_line->y;
*/
return $height;
}
/**
* Determine the frame's restricted height
*
* @return array
*/
protected function _calculate_restricted_height() {
$frame = $this->_frame;
$style = $frame->get_style();
$content_height = $this->_calculate_content_height();
$cb = $frame->get_containing_block();
$height = $style->length_in_pt($style->height, $cb["h"]);
$top = $style->length_in_pt($style->top, $cb["h"]);
$bottom = $style->length_in_pt($style->bottom, $cb["h"]);
$margin_top = $style->length_in_pt($style->margin_top, $cb["h"]);
$margin_bottom = $style->length_in_pt($style->margin_bottom, $cb["h"]);
if ( $frame->is_absolute() ) {
// see http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-height
$dims = array($top !== "auto" ? $top : 0,
$style->margin_top !== "auto" ? $style->margin_top : 0,
$style->padding_top,
$style->border_top_width,
$height !== "auto" ? $height : 0,
$style->border_bottom_width,
$style->padding_bottom,
$style->margin_bottom !== "auto" ? $style->margin_bottom : 0,
$bottom !== "auto" ? $bottom : 0);
$sum = $style->length_in_pt($dims, $cb["h"]);
$diff = $cb["h"] - $sum;
if ( $diff > 0 ) {
if ( $height === "auto" && $top === "auto" && $bottom === "auto" ) {
if ( $margin_top === "auto" ) $margin_top = 0;
if ( $margin_bottom === "auto" ) $margin_bottom = 0;
$height = $diff;
}
else if ( $height === "auto" && $top === "auto" ) {
if ( $margin_top === "auto" ) $margin_top = 0;
if ( $margin_bottom === "auto" ) $margin_bottom = 0;
$height = $content_height;
$top = $diff - $content_height;
}
else if ( $height === "auto" && $bottom === "auto" ) {
if ( $margin_top === "auto" ) $margin_top = 0;
if ( $margin_bottom === "auto" ) $margin_bottom = 0;
$height = $content_height;
$bottom = $diff - $content_height;
}
else if ( $top === "auto" && $bottom === "auto" ) {
if ( $margin_top === "auto" ) $margin_top = 0;
if ( $margin_bottom === "auto" ) $margin_bottom = 0;
$bottom = $diff;
}
else if ( $top === "auto" ) {
if ( $margin_top === "auto" ) $margin_top = 0;
if ( $margin_bottom === "auto" ) $margin_bottom = 0;
$top = $diff;
}
else if ( $height === "auto" ) {
if ( $margin_top === "auto" ) $margin_top = 0;
if ( $margin_bottom === "auto" ) $margin_bottom = 0;
$height = $diff;
}
else if ( $bottom === "auto" ) {
if ( $margin_top === "auto" ) $margin_top = 0;
if ( $margin_bottom === "auto" ) $margin_bottom = 0;
$bottom = $diff;
}
else {
if ( $style->overflow === "visible" ) {
// set all autos to zero
if ( $margin_top === "auto" ) $margin_top = 0;
if ( $margin_bottom === "auto" ) $margin_bottom = 0;
if ( $top === "auto" ) $top = 0;
if ( $bottom === "auto" ) $bottom = 0;
if ( $height === "auto" ) $height = $content_height;
}
// FIXME: overflow hidden
}
}
}
else {
// Expand the height if overflow is visible
if ( $height === "auto" && $content_height > $height /* && $style->overflow === "visible" */) {
$height = $content_height;
}
// FIXME: this should probably be moved to a seperate function as per
// _calculate_restricted_width
// Only handle min/max height if the height is independent of the frame's content
if ( !($style->overflow === "visible" ||
($style->overflow === "hidden" && $height === "auto")) ) {
$min_height = $style->min_height;
$max_height = $style->max_height;
if ( isset($cb["h"]) ) {
$min_height = $style->length_in_pt($min_height, $cb["h"]);
$max_height = $style->length_in_pt($max_height, $cb["h"]);
}
else if ( isset($cb["w"]) ) {
if ( mb_strpos($min_height, "%") !== false ) {
$min_height = 0;
}
else {
$min_height = $style->length_in_pt($min_height, $cb["w"]);
}
if ( mb_strpos($max_height, "%") !== false ) {
$max_height = "none";
}
else {
$max_height = $style->length_in_pt($max_height, $cb["w"]);
}
}
if ( $max_height !== "none" && $min_height > $max_height ) {
// Swap 'em
list($max_height, $min_height) = array($min_height, $max_height);
}
if ( $max_height !== "none" && $height > $max_height ) {
$height = $max_height;
}
if ( $height < $min_height ) {
$height = $min_height;
}
}
}
return array($height, $margin_top, $margin_bottom, $top, $bottom);
}
/**
* Adjust the justification of each of our lines.
* http://www.w3.org/TR/CSS21/text.html#propdef-text-align
*/
protected function _text_align() {
$style = $this->_frame->get_style();
$w = $this->_frame->get_containing_block("w");
$width = $style->length_in_pt($style->width, $w);
switch ($style->text_align) {
default:
case "left":
foreach ($this->_frame->get_line_boxes() as $line) {
if ( !$line->left ) {
continue;
}
foreach($line->get_frames() as $frame) {
if ( $frame instanceof Block_Frame_Decorator) {
continue;
}
$frame->set_position( $frame->get_position("x") + $line->left );
}
}
return;
case "right":
foreach ($this->_frame->get_line_boxes() as $line) {
// Move each child over by $dx
$dx = $width - $line->w - $line->right;
foreach($line->get_frames() as $frame) {
// Block frames are not aligned by text-align
if ($frame instanceof Block_Frame_Decorator) {
continue;
}
$frame->set_position( $frame->get_position("x") + $dx );
}
}
break;
case "justify":
// We justify all lines except the last one
$lines = $this->_frame->get_line_boxes(); // needs to be a variable (strict standards)
array_pop($lines);
foreach($lines as $i => $line) {
if ( $line->br ) {
unset($lines[$i]);
}
}
// One space character's width. Will be used to get a more accurate spacing
$space_width = Font_Metrics::get_text_width(" ", $style->font_family, $style->font_size);
foreach ($lines as $line) {
if ( $line->left ) {
foreach ( $line->get_frames() as $frame ) {
if ( !$frame instanceof Text_Frame_Decorator ) {
continue;
}
$frame->set_position( $frame->get_position("x") + $line->left );
}
}
// Only set the spacing if the line is long enough. This is really
// just an aesthetic choice ;)
//if ( $line["left"] + $line["w"] + $line["right"] > self::MIN_JUSTIFY_WIDTH * $width ) {
// Set the spacing for each child
if ( $line->wc > 1 ) {
$spacing = ($width - ($line->left + $line->w + $line->right) + $space_width) / ($line->wc - 1);
}
else {
$spacing = 0;
}
$dx = 0;
foreach($line->get_frames() as $frame) {
if ( !$frame instanceof Text_Frame_Decorator ) {
continue;
}
$text = $frame->get_text();
$spaces = mb_substr_count($text, " ");
$char_spacing = $style->length_in_pt($style->letter_spacing);
$_spacing = $spacing + $char_spacing;
$frame->set_position( $frame->get_position("x") + $dx );
$frame->set_text_spacing($_spacing);
$dx += $spaces * $_spacing;
}
// The line (should) now occupy the entire width
$line->w = $width;
//}
}
break;
case "center":
case "centre":
foreach ($this->_frame->get_line_boxes() as $line) {
// Centre each line by moving each frame in the line by:
$dx = ($width + $line->left - $line->w - $line->right ) / 2;
foreach ($line->get_frames() as $frame) {
// Block frames are not aligned by text-align
if ($frame instanceof Block_Frame_Decorator) {
continue;
}
$frame->set_position( $frame->get_position("x") + $dx );
}
}
break;
}
}
/**
* Align inline children vertically.
* Aligns each child vertically after each line is reflowed
*/
function vertical_align() {
$canvas = null;
foreach ( $this->_frame->get_line_boxes() as $line ) {
$height = $line->h;
foreach ( $line->get_frames() as $frame ) {
$style = $frame->get_style();
if ( $style->display !== "inline" ) {
continue;
}
$align = $frame->get_parent()->get_style()->vertical_align;
if ( !isset($canvas) ) {
$canvas = $frame->get_root()->get_dompdf()->get_canvas();
}
$baseline = $canvas->get_font_baseline($style->font_family, $style->font_size);
$y_offset = 0;
switch ($align) {
case "baseline":
$y_offset = $height*0.8 - $baseline; // The 0.8 ratio is arbitrary until we find it's meaning
break;
case "middle":
$y_offset = ($height*0.8 - $baseline) / 2;
break;
case "sub":
$y_offset = 0.3 * $height;
break;
case "super":
$y_offset = -0.2 * $height;
break;
case "text-top":
case "top": // Not strictly accurate, but good enough for now
break;
case "text-bottom":
case "bottom":
$y_offset = $height*0.8 - $baseline;
break;
}
if ( $y_offset ) {
$frame->move(0, $y_offset);
}
}
}
}
/**
* @param Frame $child
*/
function process_clear(Frame $child){
$enable_css_float = $this->get_dompdf()->get_option("enable_css_float");
if ( !$enable_css_float ) {
return;
}
$child_style = $child->get_style();
$root = $this->_frame->get_root();
// Handle "clear"
if ( $child_style->clear !== "none" ) {
$lowest_y = $root->get_lowest_float_offset($child);
// If a float is still applying, we handle it
if ( $lowest_y ) {
if ( $child->is_in_flow() ) {
$line_box = $this->_frame->get_current_line_box();
$line_box->y = $lowest_y + $child->get_margin_height();
$line_box->left = 0;
$line_box->right = 0;
}
$child->move(0, $lowest_y - $child->get_position("y"));
}
}
}
/**
* @param Frame $child
* @param float $cb_x
* @param float $cb_w
*/
function process_float(Frame $child, $cb_x, $cb_w){
$enable_css_float = $this->_frame->get_dompdf()->get_option("enable_css_float");
if ( !$enable_css_float ) {
return;
}
$child_style = $child->get_style();
$root = $this->_frame->get_root();
// Handle "float"
if ( $child_style->float !== "none" ) {
$root->add_floating_frame($child);
// Remove next frame's beginning whitespace
$next = $child->get_next_sibling();
if ( $next && $next instanceof Text_Frame_Decorator) {
$next->set_text(ltrim($next->get_text()));
}
$line_box = $this->_frame->get_current_line_box();
list($old_x, $old_y) = $child->get_position();
$float_x = $cb_x;
$float_y = $old_y;
$float_w = $child->get_margin_width();
if ( $child_style->clear === "none" ) {
switch( $child_style->float ) {
case "left":
$float_x += $line_box->left;
break;
case "right":
$float_x += ($cb_w - $line_box->right - $float_w);
break;
}
}
else {
if ( $child_style->float === "right" ) {
$float_x += ($cb_w - $float_w);
}
}
if ( $cb_w < $float_x + $float_w - $old_x ) {
// TODO handle when floating elements don't fit
}
$line_box->get_float_offsets();
if ( $child->_float_next_line ) {
$float_y += $line_box->h;
}
$child->set_position($float_x, $float_y);
$child->move($float_x - $old_x, $float_y - $old_y, true);
}
}
/**
* @param Frame_Decorator $block
*/
function reflow(Block_Frame_Decorator $block = null) {
// Check if a page break is forced
$page = $this->_frame->get_root();
$page->check_forced_page_break($this->_frame);
// Bail if the page is full
if ( $page->is_full() ) {
return;
}
// Generated content
$this->_set_content();
// Collapse margins if required
$this->_collapse_margins();
$style = $this->_frame->get_style();
$cb = $this->_frame->get_containing_block();
if ( $style->position === "fixed" ) {
$cb = $this->_frame->get_root()->get_containing_block();
}
// Determine the constraints imposed by this frame: calculate the width
// of the content area:
list($w, $left_margin, $right_margin, $left, $right) = $this->_calculate_restricted_width();
// Store the calculated properties
$style->width = $w . "pt";
$style->margin_left = $left_margin."pt";
$style->margin_right = $right_margin."pt";
$style->left = $left ."pt";
$style->right = $right . "pt";
// Update the position
$this->_frame->position();
list($x, $y) = $this->_frame->get_position();
// Adjust the first line based on the text-indent property
$indent = $style->length_in_pt($style->text_indent, $cb["w"]);
$this->_frame->increase_line_width($indent);
// Determine the content edge
$top = $style->length_in_pt(array($style->margin_top,
$style->padding_top,
$style->border_top_width), $cb["h"]);
$bottom = $style->length_in_pt(array($style->border_bottom_width,
$style->margin_bottom,
$style->padding_bottom), $cb["h"]);
$cb_x = $x + $left_margin + $style->length_in_pt(array($style->border_left_width,
$style->padding_left), $cb["w"]);
$cb_y = $y + $top;
$cb_h = ($cb["h"] + $cb["y"]) - $bottom - $cb_y;
// Set the y position of the first line in this block
$line_box = $this->_frame->get_current_line_box();
$line_box->y = $cb_y;
$line_box->get_float_offsets();
// Set the containing blocks and reflow each child
foreach ( $this->_frame->get_children() as $child ) {
// Bail out if the page is full
if ( $page->is_full() ) {
break;
}
$child->set_containing_block($cb_x, $cb_y, $w, $cb_h);
$this->process_clear($child);
$child->reflow($this->_frame);
// Don't add the child to the line if a page break has occurred
if ( $page->check_page_break($child) ) {
break;
}
$this->process_float($child, $cb_x, $w);
}
// Determine our height
list($height, $margin_top, $margin_bottom, $top, $bottom) = $this->_calculate_restricted_height();
$style->height = $height;
$style->margin_top = $margin_top;
$style->margin_bottom = $margin_bottom;
$style->top = $top;
$style->bottom = $bottom;
$needs_reposition = ($style->position === "absolute" && ($style->right !== "auto" || $style->bottom !== "auto"));
// Absolute positioning measurement
if ( $needs_reposition ) {
$orig_style = $this->_frame->get_original_style();
if ( $orig_style->width === "auto" && ($orig_style->left === "auto" || $orig_style->right === "auto") ) {
$width = 0;
foreach ($this->_frame->get_line_boxes() as $line) {
$width = max($line->w, $width);
}
$style->width = $width;
}
$style->left = $orig_style->left;
$style->right = $orig_style->right;
}
$this->_text_align();
$this->vertical_align();
// Absolute positioning
if ( $needs_reposition ) {
list($x, $y) = $this->_frame->get_position();
$this->_frame->position();
list($new_x, $new_y) = $this->_frame->get_position();
$this->_frame->move($new_x-$x, $new_y-$y, true);
}
if ( $block && $this->_frame->is_in_flow() ) {
$block->add_frame_to_line($this->_frame);
// May be inline-block
if ( $style->display === "block" ) {
$block->add_line();
}
}
}
}

View File

@@ -0,0 +1,57 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Positions block frames
*
* @access private
* @package dompdf
*/
class Block_Positioner extends Positioner {
function __construct(Frame_Decorator $frame) { parent::__construct($frame); }
//........................................................................
function position() {
$frame = $this->_frame;
$style = $frame->get_style();
$cb = $frame->get_containing_block();
$p = $frame->find_block_parent();
if ( $p ) {
$float = $style->float;
$enable_css_float = $frame->get_dompdf()->get_option("enable_css_float");
if ( !$enable_css_float || !$float || $float === "none" ) {
$p->add_line(true);
}
$y = $p->get_current_line_box()->y;
}
else {
$y = $cb["y"];
}
$x = $cb["x"];
// Relative positionning
if ( $style->position === "relative" ) {
$top = $style->length_in_pt($style->top, $cb["h"]);
//$right = $style->length_in_pt($style->right, $cb["w"]);
//$bottom = $style->length_in_pt($style->bottom, $cb["h"]);
$left = $style->length_in_pt($style->left, $cb["w"]);
$x += $left;
$y += $top;
}
$frame->set_position($x, $y);
}
}

View File

@@ -0,0 +1,230 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Renders block frames
*
* @access private
* @package dompdf
*/
class Block_Renderer extends Abstract_Renderer {
//........................................................................
function render(Frame $frame) {
$style = $frame->get_style();
$node = $frame->get_node();
list($x, $y, $w, $h) = $frame->get_border_box();
$this->_set_opacity( $frame->get_opacity( $style->opacity ) );
if ( $node->nodeName === "body" ) {
$h = $frame->get_containing_block("h") - $style->length_in_pt(array(
$style->margin_top,
$style->border_top_width,
$style->border_bottom_width,
$style->margin_bottom),
$style->width);
}
// Handle anchors & links
if ( $node->nodeName === "a" && $href = $node->getAttribute("href") ) {
$this->_canvas->add_link($href, $x, $y, $w, $h);
}
// Draw our background, border and content
list($tl, $tr, $br, $bl) = $style->get_computed_border_radius($w, $h);
if ( $tl + $tr + $br + $bl > 0 ) {
$this->_canvas->clipping_roundrectangle( $x, $y, $w, $h, $tl, $tr, $br, $bl );
}
if ( ($bg = $style->background_color) !== "transparent" ) {
$this->_canvas->filled_rectangle( $x, $y, $w, $h, $bg );
}
if ( ($url = $style->background_image) && $url !== "none" ) {
$this->_background_image($url, $x, $y, $w, $h, $style);
}
if ( $tl + $tr + $br + $bl > 0 ) {
$this->_canvas->clipping_end();
}
$border_box = array($x, $y, $w, $h);
$this->_render_border($frame, $border_box);
$this->_render_outline($frame, $border_box);
if (DEBUG_LAYOUT && DEBUG_LAYOUT_BLOCKS) {
$this->_debug_layout($frame->get_border_box(), "red");
if (DEBUG_LAYOUT_PADDINGBOX) {
$this->_debug_layout($frame->get_padding_box(), "red", array(0.5, 0.5));
}
}
if (DEBUG_LAYOUT && DEBUG_LAYOUT_LINES && $frame->get_decorator()) {
foreach ($frame->get_decorator()->get_line_boxes() as $line) {
$frame->_debug_layout(array($line->x, $line->y, $line->w, $line->h), "orange");
}
}
}
protected function _render_border(Frame_Decorator $frame, $border_box = null, $corner_style = "bevel") {
$style = $frame->get_style();
$bp = $style->get_border_properties();
if ( empty($border_box) ) {
$border_box = $frame->get_border_box();
}
// find the radius
$radius = $style->get_computed_border_radius($border_box[2], $border_box[3]); // w, h
// Short-cut: If all the borders are "solid" with the same color and style, and no radius, we'd better draw a rectangle
if (
in_array($bp["top"]["style"], array("solid", "dashed", "dotted")) &&
$bp["top"] == $bp["right"] &&
$bp["right"] == $bp["bottom"] &&
$bp["bottom"] == $bp["left"] &&
array_sum($radius) == 0
) {
$props = $bp["top"];
if ( $props["color"] === "transparent" || $props["width"] <= 0 ) return;
list($x, $y, $w, $h) = $border_box;
$width = $style->length_in_pt($props["width"]);
$pattern = $this->_get_dash_pattern($props["style"], $width);
$this->_canvas->rectangle($x + $width / 2, $y + $width / 2, $w - $width, $h - $width, $props["color"], $width, $pattern);
return;
}
// Do it the long way
$widths = array($style->length_in_pt($bp["top"]["width"]),
$style->length_in_pt($bp["right"]["width"]),
$style->length_in_pt($bp["bottom"]["width"]),
$style->length_in_pt($bp["left"]["width"]));
foreach ($bp as $side => $props) {
list($x, $y, $w, $h) = $border_box;
$length = 0;
$r1 = 0;
$r2 = 0;
if ( !$props["style"] ||
$props["style"] === "none" ||
$props["width"] <= 0 ||
$props["color"] == "transparent" )
continue;
switch($side) {
case "top":
$length = $w;
$r1 = $radius["top-left"];
$r2 = $radius["top-right"];
break;
case "bottom":
$length = $w;
$y += $h;
$r1 = $radius["bottom-left"];
$r2 = $radius["bottom-right"];
break;
case "left":
$length = $h;
$r1 = $radius["top-left"];
$r2 = $radius["bottom-left"];
break;
case "right":
$length = $h;
$x += $w;
$r1 = $radius["top-right"];
$r2 = $radius["bottom-right"];
break;
default:
break;
}
$method = "_border_" . $props["style"];
// draw rounded corners
$this->$method($x, $y, $length, $props["color"], $widths, $side, $corner_style, $r1, $r2);
}
}
protected function _render_outline(Frame_Decorator $frame, $border_box = null, $corner_style = "bevel") {
$style = $frame->get_style();
$props = array(
"width" => $style->outline_width,
"style" => $style->outline_style,
"color" => $style->outline_color,
);
if ( !$props["style"] || $props["style"] === "none" || $props["width"] <= 0 )
return;
if ( empty($border_box) ) {
$border_box = $frame->get_border_box();
}
$offset = $style->length_in_pt($props["width"]);
$pattern = $this->_get_dash_pattern($props["style"], $offset);
// If the outline style is "solid" we'd better draw a rectangle
if ( in_array($props["style"], array("solid", "dashed", "dotted")) ) {
$border_box[0] -= $offset / 2;
$border_box[1] -= $offset / 2;
$border_box[2] += $offset;
$border_box[3] += $offset;
list($x, $y, $w, $h) = $border_box;
$this->_canvas->rectangle($x, $y, $w, $h, $props["color"], $offset, $pattern);
return;
}
$border_box[0] -= $offset;
$border_box[1] -= $offset;
$border_box[2] += $offset * 2;
$border_box[3] += $offset * 2;
$method = "_border_" . $props["style"];
$widths = array_fill(0, 4, $props["width"]);
$sides = array("top", "right", "left", "bottom");
$length = 0;
foreach ($sides as $side) {
list($x, $y, $w, $h) = $border_box;
switch($side) {
case "top":
$length = $w;
break;
case "bottom":
$length = $w;
$y += $h;
break;
case "left":
$length = $h;
break;
case "right":
$length = $h;
$x += $w;
break;
default:
break;
}
$this->$method($x, $y, $length, $props["color"], $widths, $side, $corner_style);
}
}
}

View File

@@ -0,0 +1,164 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Caching canvas implementation
*
* Each rendered page is serialized and stored in the {@link Page_Cache}.
* This is useful for static forms/pages that do not need to be re-rendered
* all the time.
*
* This class decorates normal CPDF_Adapters. It is currently completely
* experimental.
*
* @access private
* @package dompdf
*/
class Cached_PDF_Decorator extends CPDF_Adapter implements Canvas {
/**
* @var CPDF_Adapter
*/
protected $_pdf;
protected $_cache_id;
protected $_current_page_id;
protected $_fonts; // fonts used in this document
function __construct($paper = "letter", $orientation = "portrait", DOMPDF $dompdf) {
$this->_fonts = array();
}
/**
* Must be called after constructor
*
* @param int $cache_id
* @param CPDF_Adapter $pdf
*/
function init($cache_id, CPDF_Adapter $pdf) {
$this->_cache_id = $cache_id;
$this->_pdf = $pdf;
$this->_current_page_id = $this->_pdf->open_object();
}
//........................................................................
function get_cpdf() { return $this->_pdf->get_cpdf(); }
function open_object() { $this->_pdf->open_object(); }
function reopen_object($object) { $this->_pdf->reopen_object($object); }
function close_object() { $this->_pdf->close_object(); }
function add_object($object, $where = 'all') { $this->_pdf->add_object($object, $where); }
function serialize_object($id) { $this->_pdf->serialize_object($id); }
function reopen_serialized_object($obj) { $this->_pdf->reopen_serialized_object($obj); }
//........................................................................
function get_width() { return $this->_pdf->get_width(); }
function get_height() { return $this->_pdf->get_height(); }
function get_page_number() { return $this->_pdf->get_page_number(); }
function get_page_count() { return $this->_pdf->get_page_count(); }
function set_page_number($num) { $this->_pdf->set_page_number($num); }
function set_page_count($count) { $this->_pdf->set_page_count($count); }
function line($x1, $y1, $x2, $y2, $color, $width, $style = array()) {
$this->_pdf->line($x1, $y1, $x2, $y2, $color, $width, $style);
}
function rectangle($x1, $y1, $w, $h, $color, $width, $style = array()) {
$this->_pdf->rectangle($x1, $y1, $w, $h, $color, $width, $style);
}
function filled_rectangle($x1, $y1, $w, $h, $color) {
$this->_pdf->filled_rectangle($x1, $y1, $w, $h, $color);
}
function polygon($points, $color, $width = null, $style = array(), $fill = false) {
$this->_pdf->polygon($points, $color, $width, $style, $fill);
}
function circle($x, $y, $r1, $color, $width = null, $style = null, $fill = false) {
$this->_pdf->circle($x, $y, $r1, $color, $width, $style, $fill);
}
function image($img_url, $x, $y, $w, $h, $resolution = "normal") {
$this->_pdf->image($img_url, $x, $y, $w, $h, $resolution);
}
function text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0) {
$this->_fonts[$font] = true;
$this->_pdf->text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle);
}
function page_text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0) {
// We want to remove this from cached pages since it may not be correct
$this->_pdf->close_object();
$this->_pdf->page_text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle);
$this->_pdf->reopen_object($this->_current_page_id);
}
function page_script($script, $type = 'text/php') {
// We want to remove this from cached pages since it may not be correct
$this->_pdf->close_object();
$this->_pdf->page_script($script, $type);
$this->_pdf->reopen_object($this->_current_page_id);
}
function new_page() {
$this->_pdf->close_object();
// Add the object to the current page
$this->_pdf->add_object($this->_current_page_id, "add");
$this->_pdf->new_page();
Page_Cache::store_page($this->_cache_id,
$this->_pdf->get_page_number() - 1,
$this->_pdf->serialize_object($this->_current_page_id));
$this->_current_page_id = $this->_pdf->open_object();
return $this->_current_page_id;
}
function stream($filename, $options = null) {
// Store the last page in the page cache
if ( !is_null($this->_current_page_id) ) {
$this->_pdf->close_object();
$this->_pdf->add_object($this->_current_page_id, "add");
Page_Cache::store_page($this->_cache_id,
$this->_pdf->get_page_number(),
$this->_pdf->serialize_object($this->_current_page_id));
Page_Cache::store_fonts($this->_cache_id, $this->_fonts);
$this->_current_page_id = null;
}
$this->_pdf->stream($filename);
}
function output($options = null) {
// Store the last page in the page cache
if ( !is_null($this->_current_page_id) ) {
$this->_pdf->close_object();
$this->_pdf->add_object($this->_current_page_id, "add");
Page_Cache::store_page($this->_cache_id,
$this->_pdf->get_page_number(),
$this->_pdf->serialize_object($this->_current_page_id));
$this->_current_page_id = null;
}
return $this->_pdf->output();
}
function get_messages() { return $this->_pdf->get_messages(); }
}

View File

@@ -0,0 +1,385 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author Fabien Ménager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Main rendering interface
*
* Currently {@link CPDF_Adapter}, {@link PDFLib_Adapter}, {@link TCPDF_Adapter}, and {@link GD_Adapter}
* implement this interface.
*
* Implementations should measure x and y increasing to the left and down,
* respectively, with the origin in the top left corner. Implementations
* are free to use a unit other than points for length, but I can't
* guarantee that the results will look any good.
*
* @package dompdf
*/
interface Canvas {
function __construct($paper = "letter", $orientation = "portrait", DOMPDF $dompdf);
/**
* @return DOMPDF
*/
function get_dompdf();
/**
* Returns the current page number
*
* @return int
*/
function get_page_number();
/**
* Returns the total number of pages
*
* @return int
*/
function get_page_count();
/**
* Sets the total number of pages
*
* @param int $count
*/
function set_page_count($count);
/**
* Draws a line from x1,y1 to x2,y2
*
* See {@link Style::munge_color()} for the format of the color array.
* See {@link Cpdf::setLineStyle()} for a description of the format of the
* $style parameter (aka dash).
*
* @param float $x1
* @param float $y1
* @param float $x2
* @param float $y2
* @param array $color
* @param float $width
* @param array $style
*/
function line($x1, $y1, $x2, $y2, $color, $width, $style = null);
/**
* Draws a rectangle at x1,y1 with width w and height h
*
* See {@link Style::munge_color()} for the format of the color array.
* See {@link Cpdf::setLineStyle()} for a description of the $style
* parameter (aka dash)
*
* @param float $x1
* @param float $y1
* @param float $w
* @param float $h
* @param array $color
* @param float $width
* @param array $style
*/
function rectangle($x1, $y1, $w, $h, $color, $width, $style = null);
/**
* Draws a filled rectangle at x1,y1 with width w and height h
*
* See {@link Style::munge_color()} for the format of the color array.
*
* @param float $x1
* @param float $y1
* @param float $w
* @param float $h
* @param array $color
*/
function filled_rectangle($x1, $y1, $w, $h, $color);
/**
* Starts a clipping rectangle at x1,y1 with width w and height h
*
* @param float $x1
* @param float $y1
* @param float $w
* @param float $h
*/
function clipping_rectangle($x1, $y1, $w, $h);
/**
* Starts a rounded clipping rectangle at x1,y1 with width w and height h
*
* @param float $x1
* @param float $y1
* @param float $w
* @param float $h
* @param float $tl
* @param float $tr
* @param float $br
* @param float $bl
*
* @return
*/
function clipping_roundrectangle($x1, $y1, $w, $h, $tl, $tr, $br, $bl);
/**
* Ends the last clipping shape
*/
function clipping_end();
/**
* Save current state
*/
function save();
/**
* Restore last state
*/
function restore();
/**
* Rotate
*/
function rotate($angle, $x, $y);
/**
* Skew
*/
function skew($angle_x, $angle_y, $x, $y);
/**
* Scale
*/
function scale($s_x, $s_y, $x, $y);
/**
* Translate
*/
function translate($t_x, $t_y);
/**
* Transform
*/
function transform($a, $b, $c, $d, $e, $f);
/**
* Draws a polygon
*
* The polygon is formed by joining all the points stored in the $points
* array. $points has the following structure:
* <code>
* array(0 => x1,
* 1 => y1,
* 2 => x2,
* 3 => y2,
* ...
* );
* </code>
*
* See {@link Style::munge_color()} for the format of the color array.
* See {@link Cpdf::setLineStyle()} for a description of the $style
* parameter (aka dash)
*
* @param array $points
* @param array $color
* @param float $width
* @param array $style
* @param bool $fill Fills the polygon if true
*/
function polygon($points, $color, $width = null, $style = null, $fill = false);
/**
* Draws a circle at $x,$y with radius $r
*
* See {@link Style::munge_color()} for the format of the color array.
* See {@link Cpdf::setLineStyle()} for a description of the $style
* parameter (aka dash)
*
* @param float $x
* @param float $y
* @param float $r
* @param array $color
* @param float $width
* @param array $style
* @param bool $fill Fills the circle if true
*/
function circle($x, $y, $r, $color, $width = null, $style = null, $fill = false);
/**
* Add an image to the pdf.
*
* The image is placed at the specified x and y coordinates with the
* given width and height.
*
* @param string $img_url the path to the image
* @param float $x x position
* @param float $y y position
* @param int $w width (in pixels)
* @param int $h height (in pixels)
* @param string $resolution The resolution of the image
*/
function image($img_url, $x, $y, $w, $h, $resolution = "normal");
/**
* Add an arc to the PDF
* See {@link Style::munge_color()} for the format of the color array.
*
* @param float $x X coordinate of the arc
* @param float $y Y coordinate of the arc
* @param float $r1 Radius 1
* @param float $r2 Radius 2
* @param float $astart Start angle in degrees
* @param float $aend End angle in degrees
* @param array $color Color
* @param float $width
* @param array $style
*
* @return void
*/
function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = array());
/**
* Writes text at the specified x and y coordinates
* See {@link Style::munge_color()} for the format of the color array.
*
* @param float $x
* @param float $y
* @param string $text the text to write
* @param string $font the font file to use
* @param float $size the font size, in points
* @param array $color
* @param float $word_space word spacing adjustment
* @param float $char_space char spacing adjustment
* @param float $angle angle
*
* @return void
*/
function text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0);
/**
* Add a named destination (similar to <a name="foo">...</a> in html)
*
* @param string $anchorname The name of the named destination
*/
function add_named_dest($anchorname);
/**
* Add a link to the pdf
*
* @param string $url The url to link to
* @param float $x The x position of the link
* @param float $y The y position of the link
* @param float $width The width of the link
* @param float $height The height of the link
*
* @return void
*/
function add_link($url, $x, $y, $width, $height);
/**
* Add meta information to the pdf
*
* @param string $name Label of the value (Creator, Producer, etc.)
* @param string $value The text to set
*/
function add_info($name, $value);
/**
* Calculates text size, in points
*
* @param string $text the text to be sized
* @param string $font the desired font
* @param float $size the desired font size
* @param float $word_spacing word spacing, if any
* @param float $char_spacing
*
* @return float
*/
function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0);
/**
* Calculates font height, in points
*
* @param string $font
* @param float $size
*
* @return float
*/
function get_font_height($font, $size);
/**
* Calculates font baseline, in points
*
* @param string $font
* @param float $size
*
* @return float
*/
function get_font_baseline($font, $size);
/**
* Returns the font x-height, in points
*
* @param string $font
* @param float $size
*
* @return float
*/
//function get_font_x_height($font, $size);
/**
* Sets the opacity
*
* @param float $opacity
* @param string $mode
*/
function set_opacity($opacity, $mode = "Normal");
/**
* Sets the default view
*
* @param string $view
* 'XYZ' left, top, zoom
* 'Fit'
* 'FitH' top
* 'FitV' left
* 'FitR' left,bottom,right
* 'FitB'
* 'FitBH' top
* 'FitBV' left
* @param array $options
*
* @return void
*/
function set_default_view($view, $options = array());
/**
* @param string $script
*
* @return void
*/
function javascript($script);
/**
* Starts a new page
*
* Subsequent drawing operations will appear on the new page.
*/
function new_page();
/**
* Streams the PDF directly to the browser
*
* @param string $filename the name of the PDF file
* @param array $options associative array, 'Attachment' => 0 or 1, 'compress' => 1 or 0
*/
function stream($filename, $options = null);
/**
* Returns the PDF as a string
*
* @param array $options associative array: 'compress' => 1 or 0
* @return string
*/
function output($options = null);
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Create canvas instances
*
* The canvas factory creates canvas instances based on the
* availability of rendering backends and config options.
*
* @package dompdf
*/
class Canvas_Factory {
/**
* Constructor is private: this is a static class
*/
private function __construct() { }
/**
* @param DOMPDF $dompdf
* @param string|array $paper
* @param string $orientation
* @param string $class
*
* @return Canvas
*/
static function get_instance(DOMPDF $dompdf, $paper = null, $orientation = null, $class = null) {
$backend = strtolower(DOMPDF_PDF_BACKEND);
if ( isset($class) && class_exists($class, false) ) {
$class .= "_Adapter";
}
else if ( (DOMPDF_PDF_BACKEND === "auto" || $backend === "pdflib" ) &&
class_exists("PDFLib", false) ) {
$class = "PDFLib_Adapter";
}
// FIXME The TCPDF adapter is not ready yet
//else if ( (DOMPDF_PDF_BACKEND === "auto" || $backend === "cpdf") )
// $class = "CPDF_Adapter";
else if ( $backend === "tcpdf" ) {
$class = "TCPDF_Adapter";
}
else if ( $backend === "gd" ) {
$class = "GD_Adapter";
}
else {
$class = "CPDF_Adapter";
}
return new $class($paper, $orientation, $dompdf);
}
}

View File

@@ -0,0 +1,790 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Maps table cells to the table grid.
*
* This class resolves borders in tables with collapsed borders and helps
* place row & column spanned table cells.
*
* @access private
* @package dompdf
*/
class Cellmap {
/**
* Border style weight lookup for collapsed border resolution.
*
* @var array
*/
static protected $_BORDER_STYLE_SCORE = array(
"inset" => 1,
"groove" => 2,
"outset" => 3,
"ridge" => 4,
"dotted" => 5,
"dashed" => 6,
"solid" => 7,
"double" => 8,
"hidden" => 9,
"none" => 0,
);
/**
* The table object this cellmap is attached to.
*
* @var Table_Frame_Decorator
*/
protected $_table;
/**
* The total number of rows in the table
*
* @var int
*/
protected $_num_rows;
/**
* The total number of columns in the table
*
* @var int
*/
protected $_num_cols;
/**
* 2D array mapping <row,column> to frames
*
* @var Frame[][]
*/
protected $_cells;
/**
* 1D array of column dimensions
*
* @var array
*/
protected $_columns;
/**
* 1D array of row dimensions
*
* @var array
*/
protected $_rows;
/**
* 2D array of border specs
*
* @var array
*/
protected $_borders;
/**
* 1D Array mapping frames to (multiple) <row, col> pairs, keyed on frame_id.
*
* @var Frame[]
*/
protected $_frames;
/**
* Current column when adding cells, 0-based
*
* @var int
*/
private $__col;
/**
* Current row when adding cells, 0-based
*
* @var int
*/
private $__row;
/**
* Tells wether the columns' width can be modified
*
* @var bool
*/
private $_columns_locked = false;
/**
* Tells wether the table has table-layout:fixed
*
* @var bool
*/
private $_fixed_layout = false;
//........................................................................
function __construct(Table_Frame_Decorator $table) {
$this->_table = $table;
$this->reset();
}
function __destruct() {
clear_object($this);
}
//........................................................................
function reset() {
$this->_num_rows = 0;
$this->_num_cols = 0;
$this->_cells = array();
$this->_frames = array();
if ( !$this->_columns_locked ) {
$this->_columns = array();
}
$this->_rows = array();
$this->_borders = array();
$this->__col = $this->__row = 0;
}
//........................................................................
function lock_columns() {
$this->_columns_locked = true;
}
function is_columns_locked() {
return $this->_columns_locked;
}
function set_layout_fixed($fixed) {
$this->_fixed_layout = $fixed;
}
function is_layout_fixed() {
return $this->_fixed_layout;
}
function get_num_rows() { return $this->_num_rows; }
function get_num_cols() { return $this->_num_cols; }
function &get_columns() {
return $this->_columns;
}
function set_columns($columns) {
$this->_columns = $columns;
}
function &get_column($i) {
if ( !isset($this->_columns[$i]) ) {
$this->_columns[$i] = array(
"x" => 0,
"min-width" => 0,
"max-width" => 0,
"used-width" => null,
"absolute" => 0,
"percent" => 0,
"auto" => true,
);
}
return $this->_columns[$i];
}
function &get_rows() {
return $this->_rows;
}
function &get_row($j) {
if ( !isset($this->_rows[$j]) ) {
$this->_rows[$j] = array(
"y" => 0,
"first-column" => 0,
"height" => null,
);
}
return $this->_rows[$j];
}
function get_border($i, $j, $h_v, $prop = null) {
if ( !isset($this->_borders[$i][$j][$h_v]) ) {
$this->_borders[$i][$j][$h_v] = array(
"width" => 0,
"style" => "solid",
"color" => "black",
);
}
if ( isset($prop) ) {
return $this->_borders[$i][$j][$h_v][$prop];
}
return $this->_borders[$i][$j][$h_v];
}
function get_border_properties($i, $j) {
return array(
"top" => $this->get_border($i, $j, "horizontal"),
"right" => $this->get_border($i, $j+1, "vertical"),
"bottom" => $this->get_border($i+1, $j, "horizontal"),
"left" => $this->get_border($i, $j, "vertical"),
);
}
//........................................................................
function get_spanned_cells(Frame $frame) {
$key = $frame->get_id();
if ( !isset($this->_frames[$key]) ) {
throw new DOMPDF_Exception("Frame not found in cellmap");
}
return $this->_frames[$key];
}
function frame_exists_in_cellmap(Frame $frame) {
$key = $frame->get_id();
return isset($this->_frames[$key]);
}
function get_frame_position(Frame $frame) {
global $_dompdf_warnings;
$key = $frame->get_id();
if ( !isset($this->_frames[$key]) ) {
throw new DOMPDF_Exception("Frame not found in cellmap");
}
$col = $this->_frames[$key]["columns"][0];
$row = $this->_frames[$key]["rows"][0];
if ( !isset($this->_columns[$col])) {
$_dompdf_warnings[] = "Frame not found in columns array. Check your table layout for missing or extra TDs.";
$x = 0;
}
else {
$x = $this->_columns[$col]["x"];
}
if ( !isset($this->_rows[$row])) {
$_dompdf_warnings[] = "Frame not found in row array. Check your table layout for missing or extra TDs.";
$y = 0;
}
else {
$y = $this->_rows[$row]["y"];
}
return array($x, $y, "x" => $x, "y" => $y);
}
function get_frame_width(Frame $frame) {
$key = $frame->get_id();
if ( !isset($this->_frames[$key]) ) {
throw new DOMPDF_Exception("Frame not found in cellmap");
}
$cols = $this->_frames[$key]["columns"];
$w = 0;
foreach ($cols as $i) {
$w += $this->_columns[$i]["used-width"];
}
return $w;
}
function get_frame_height(Frame $frame) {
$key = $frame->get_id();
if ( !isset($this->_frames[$key]) ) {
throw new DOMPDF_Exception("Frame not found in cellmap");
}
$rows = $this->_frames[$key]["rows"];
$h = 0;
foreach ($rows as $i) {
if ( !isset($this->_rows[$i]) ) {
throw new Exception("The row #$i could not be found, please file an issue in the tracker with the HTML code");
}
$h += $this->_rows[$i]["height"];
}
return $h;
}
//........................................................................
function set_column_width($j, $width) {
if ( $this->_columns_locked ) {
return;
}
$col =& $this->get_column($j);
$col["used-width"] = $width;
$next_col =& $this->get_column($j+1);
$next_col["x"] = $next_col["x"] + $width;
}
function set_row_height($i, $height) {
$row =& $this->get_row($i);
if ( $row["height"] !== null && $height <= $row["height"] ) {
return;
}
$row["height"] = $height;
$next_row =& $this->get_row($i+1);
$next_row["y"] = $row["y"] + $height;
}
//........................................................................
protected function _resolve_border($i, $j, $h_v, $border_spec) {
$n_width = $border_spec["width"];
$n_style = $border_spec["style"];
if ( !isset($this->_borders[$i][$j][$h_v]) ) {
$this->_borders[$i][$j][$h_v] = $border_spec;
return $this->_borders[$i][$j][$h_v]["width"];
}
$border = &$this->_borders[$i][$j][$h_v];
$o_width = $border["width"];
$o_style = $border["style"];
if ( ($n_style === "hidden" ||
$n_width > $o_width ||
$o_style === "none")
or
($o_width == $n_width &&
in_array($n_style, self::$_BORDER_STYLE_SCORE) &&
self::$_BORDER_STYLE_SCORE[ $n_style ] > self::$_BORDER_STYLE_SCORE[ $o_style ]) ) {
$border = $border_spec;
}
return $border["width"];
}
//........................................................................
function add_frame(Frame $frame) {
$style = $frame->get_style();
$display = $style->display;
$collapse = $this->_table->get_style()->border_collapse == "collapse";
// Recursively add the frames within tables, table-row-groups and table-rows
if ( $display === "table-row" ||
$display === "table" ||
$display === "inline-table" ||
in_array($display, Table_Frame_Decorator::$ROW_GROUPS) ) {
$start_row = $this->__row;
foreach ( $frame->get_children() as $child ) {
$this->add_frame( $child );
}
if ( $display === "table-row" ) {
$this->add_row();
}
$num_rows = $this->__row - $start_row - 1;
$key = $frame->get_id();
// Row groups always span across the entire table
$this->_frames[$key]["columns"] = range(0,max(0,$this->_num_cols-1));
$this->_frames[$key]["rows"] = range($start_row, max(0, $this->__row - 1));
$this->_frames[$key]["frame"] = $frame;
if ( $display !== "table-row" && $collapse ) {
$bp = $style->get_border_properties();
// Resolve the borders
for ( $i = 0; $i < $num_rows+1; $i++) {
$this->_resolve_border($start_row + $i, 0, "vertical", $bp["left"]);
$this->_resolve_border($start_row + $i, $this->_num_cols, "vertical", $bp["right"]);
}
for ( $j = 0; $j < $this->_num_cols; $j++) {
$this->_resolve_border($start_row, $j, "horizontal", $bp["top"]);
$this->_resolve_border($this->__row, $j, "horizontal", $bp["bottom"]);
}
}
return;
}
$node = $frame->get_node();
// Determine where this cell is going
$colspan = $node->getAttribute("colspan");
$rowspan = $node->getAttribute("rowspan");
if ( !$colspan ) {
$colspan = 1;
$node->setAttribute("colspan",1);
}
if ( !$rowspan ) {
$rowspan = 1;
$node->setAttribute("rowspan",1);
}
$key = $frame->get_id();
$bp = $style->get_border_properties();
// Add the frame to the cellmap
$max_left = $max_right = 0;
// Find the next available column (fix by Ciro Mondueri)
$ac = $this->__col;
while ( isset($this->_cells[$this->__row][$ac]) ) {
$ac++;
}
$this->__col = $ac;
// Rows:
for ( $i = 0; $i < $rowspan; $i++ ) {
$row = $this->__row + $i;
$this->_frames[$key]["rows"][] = $row;
for ( $j = 0; $j < $colspan; $j++) {
$this->_cells[$row][$this->__col + $j] = $frame;
}
if ( $collapse ) {
// Resolve vertical borders
$max_left = max($max_left, $this->_resolve_border($row, $this->__col, "vertical", $bp["left"]));
$max_right = max($max_right, $this->_resolve_border($row, $this->__col + $colspan, "vertical", $bp["right"]));
}
}
$max_top = $max_bottom = 0;
// Columns:
for ( $j = 0; $j < $colspan; $j++ ) {
$col = $this->__col + $j;
$this->_frames[$key]["columns"][] = $col;
if ( $collapse ) {
// Resolve horizontal borders
$max_top = max($max_top, $this->_resolve_border($this->__row, $col, "horizontal", $bp["top"]));
$max_bottom = max($max_bottom, $this->_resolve_border($this->__row + $rowspan, $col, "horizontal", $bp["bottom"]));
}
}
$this->_frames[$key]["frame"] = $frame;
// Handle seperated border model
if ( !$collapse ) {
list($h, $v) = $this->_table->get_style()->border_spacing;
// Border spacing is effectively a margin between cells
$v = $style->length_in_pt($v) / 2;
$h = $style->length_in_pt($h) / 2;
$style->margin = "$v $h";
// The additional 1/2 width gets added to the table proper
}
else {
// Drop the frame's actual border
$style->border_left_width = $max_left / 2;
$style->border_right_width = $max_right / 2;
$style->border_top_width = $max_top / 2;
$style->border_bottom_width = $max_bottom / 2;
$style->margin = "none";
}
if ( !$this->_columns_locked ) {
// Resolve the frame's width
if ( $this->_fixed_layout ) {
list($frame_min, $frame_max) = array(0, 10e-10);
}
else {
list($frame_min, $frame_max) = $frame->get_min_max_width();
}
$width = $style->width;
$val = null;
if ( is_percent($width) ) {
$var = "percent";
$val = (float)rtrim($width, "% ") / $colspan;
}
else if ( $width !== "auto" ) {
$var = "absolute";
$val = $style->length_in_pt($frame_min) / $colspan;
}
$min = 0;
$max = 0;
for ( $cs = 0; $cs < $colspan; $cs++ ) {
// Resolve the frame's width(s) with other cells
$col =& $this->get_column( $this->__col + $cs );
// Note: $var is either 'percent' or 'absolute'. We compare the
// requested percentage or absolute values with the existing widths
// and adjust accordingly.
if ( isset($var) && $val > $col[$var] ) {
$col[$var] = $val;
$col["auto"] = false;
}
$min += $col["min-width"];
$max += $col["max-width"];
}
if ( $frame_min > $min ) {
// The frame needs more space. Expand each sub-column
// FIXME try to avoid putting this dummy value when table-layout:fixed
$inc = ($this->is_layout_fixed() ? 10e-10 : ($frame_min - $min) / $colspan);
for ($c = 0; $c < $colspan; $c++) {
$col =& $this->get_column($this->__col + $c);
$col["min-width"] += $inc;
}
}
if ( $frame_max > $max ) {
// FIXME try to avoid putting this dummy value when table-layout:fixed
$inc = ($this->is_layout_fixed() ? 10e-10 : ($frame_max - $max) / $colspan);
for ($c = 0; $c < $colspan; $c++) {
$col =& $this->get_column($this->__col + $c);
$col["max-width"] += $inc;
}
}
}
$this->__col += $colspan;
if ( $this->__col > $this->_num_cols )
$this->_num_cols = $this->__col;
}
//........................................................................
function add_row() {
$this->__row++;
$this->_num_rows++;
// Find the next available column
$i = 0;
while ( isset($this->_cells[$this->__row][$i]) ) {
$i++;
}
$this->__col = $i;
}
//........................................................................
/**
* Remove a row from the cellmap.
*
* @param Frame
*/
function remove_row(Frame $row) {
$key = $row->get_id();
if ( !isset($this->_frames[$key]) ) {
return; // Presumably this row has alredy been removed
}
$this->_row = $this->_num_rows--;
$rows = $this->_frames[$key]["rows"];
$columns = $this->_frames[$key]["columns"];
// Remove all frames from this row
foreach ( $rows as $r ) {
foreach ( $columns as $c ) {
if ( isset($this->_cells[$r][$c]) ) {
$id = $this->_cells[$r][$c]->get_id();
$this->_frames[$id] = null;
unset($this->_frames[$id]);
$this->_cells[$r][$c] = null;
unset($this->_cells[$r][$c]);
}
}
$this->_rows[$r] = null;
unset($this->_rows[$r]);
}
$this->_frames[$key] = null;
unset($this->_frames[$key]);
}
/**
* Remove a row group from the cellmap.
*
* @param Frame $group The group to remove
*/
function remove_row_group(Frame $group) {
$key = $group->get_id();
if ( !isset($this->_frames[$key]) ) {
return; // Presumably this row has alredy been removed
}
$iter = $group->get_first_child();
while ($iter) {
$this->remove_row($iter);
$iter = $iter->get_next_sibling();
}
$this->_frames[$key] = null;
unset($this->_frames[$key]);
}
/**
* Update a row group after rows have been removed
*
* @param Frame $group The group to update
* @param Frame $last_row The last row in the row group
*/
function update_row_group(Frame $group, Frame $last_row) {
$g_key = $group->get_id();
$r_key = $last_row->get_id();
$r_rows = $this->_frames[$r_key]["rows"];
$this->_frames[$g_key]["rows"] = range( $this->_frames[$g_key]["rows"][0], end($r_rows) );
}
//........................................................................
function assign_x_positions() {
// Pre-condition: widths must be resolved and assigned to columns and
// column[0]["x"] must be set.
if ( $this->_columns_locked ) {
return;
}
$x = $this->_columns[0]["x"];
foreach ( array_keys($this->_columns) as $j ) {
$this->_columns[$j]["x"] = $x;
$x += $this->_columns[$j]["used-width"];
}
}
function assign_frame_heights() {
// Pre-condition: widths and heights of each column & row must be
// calcluated
foreach ( $this->_frames as $arr ) {
$frame = $arr["frame"];
$h = 0;
foreach( $arr["rows"] as $row ) {
if ( !isset($this->_rows[$row]) ) {
// The row has been removed because of a page split, so skip it.
continue;
}
$h += $this->_rows[$row]["height"];
}
if ( $frame instanceof Table_Cell_Frame_Decorator ) {
$frame->set_cell_height($h);
}
else {
$frame->get_style()->height = $h;
}
}
}
//........................................................................
/**
* Re-adjust frame height if the table height is larger than its content
*/
function set_frame_heights($table_height, $content_height) {
// Distribute the increased height proportionally amongst each row
foreach ( $this->_frames as $arr ) {
$frame = $arr["frame"];
$h = 0;
foreach ($arr["rows"] as $row ) {
if ( !isset($this->_rows[$row]) ) {
continue;
}
$h += $this->_rows[$row]["height"];
}
if ( $content_height > 0 ) {
$new_height = ($h / $content_height) * $table_height;
}
else {
$new_height = 0;
}
if ( $frame instanceof Table_Cell_Frame_Decorator ) {
$frame->set_cell_height($new_height);
}
else {
$frame->get_style()->height = $new_height;
}
}
}
//........................................................................
// Used for debugging:
function __toString() {
$str = "";
$str .= "Columns:<br/>";
$str .= pre_r($this->_columns, true);
$str .= "Rows:<br/>";
$str .= pre_r($this->_rows, true);
$str .= "Frames:<br/>";
$arr = array();
foreach ( $this->_frames as $key => $val ) {
$arr[$key] = array("columns" => $val["columns"], "rows" => $val["rows"]);
}
$str .= pre_r($arr, true);
if ( php_sapi_name() == "cli" ) {
$str = strip_tags(str_replace(array("<br/>","<b>","</b>"),
array("\n",chr(27)."[01;33m", chr(27)."[0m"),
$str));
}
return $str;
}
}

View File

@@ -0,0 +1,877 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author Orion Richardson <orionr@yahoo.com>
* @author Helmut Tischer <htischer@weihenstephan.org>
* @author Fabien Ménager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
// FIXME: Need to sanity check inputs to this class
require_once(DOMPDF_LIB_DIR . "/class.pdf.php");
/**
* PDF rendering interface
*
* CPDF_Adapter provides a simple stateless interface to the stateful one
* provided by the Cpdf class.
*
* Unless otherwise mentioned, all dimensions are in points (1/72 in). The
* coordinate origin is in the top left corner, and y values increase
* downwards.
*
* See {@link http://www.ros.co.nz/pdf/} for more complete documentation
* on the underlying {@link Cpdf} class.
*
* @package dompdf
*/
class CPDF_Adapter implements Canvas {
/**
* Dimensions of paper sizes in points
*
* @var array;
*/
static $PAPER_SIZES = array(
"4a0" => array(0,0,4767.87,6740.79),
"2a0" => array(0,0,3370.39,4767.87),
"a0" => array(0,0,2383.94,3370.39),
"a1" => array(0,0,1683.78,2383.94),
"a2" => array(0,0,1190.55,1683.78),
"a3" => array(0,0,841.89,1190.55),
"a4" => array(0,0,595.28,841.89),
"a5" => array(0,0,419.53,595.28),
"a6" => array(0,0,297.64,419.53),
"a7" => array(0,0,209.76,297.64),
"a8" => array(0,0,147.40,209.76),
"a9" => array(0,0,104.88,147.40),
"a10" => array(0,0,73.70,104.88),
"b0" => array(0,0,2834.65,4008.19),
"b1" => array(0,0,2004.09,2834.65),
"b2" => array(0,0,1417.32,2004.09),
"b3" => array(0,0,1000.63,1417.32),
"b4" => array(0,0,708.66,1000.63),
"b5" => array(0,0,498.90,708.66),
"b6" => array(0,0,354.33,498.90),
"b7" => array(0,0,249.45,354.33),
"b8" => array(0,0,175.75,249.45),
"b9" => array(0,0,124.72,175.75),
"b10" => array(0,0,87.87,124.72),
"c0" => array(0,0,2599.37,3676.54),
"c1" => array(0,0,1836.85,2599.37),
"c2" => array(0,0,1298.27,1836.85),
"c3" => array(0,0,918.43,1298.27),
"c4" => array(0,0,649.13,918.43),
"c5" => array(0,0,459.21,649.13),
"c6" => array(0,0,323.15,459.21),
"c7" => array(0,0,229.61,323.15),
"c8" => array(0,0,161.57,229.61),
"c9" => array(0,0,113.39,161.57),
"c10" => array(0,0,79.37,113.39),
"ra0" => array(0,0,2437.80,3458.27),
"ra1" => array(0,0,1729.13,2437.80),
"ra2" => array(0,0,1218.90,1729.13),
"ra3" => array(0,0,864.57,1218.90),
"ra4" => array(0,0,609.45,864.57),
"sra0" => array(0,0,2551.18,3628.35),
"sra1" => array(0,0,1814.17,2551.18),
"sra2" => array(0,0,1275.59,1814.17),
"sra3" => array(0,0,907.09,1275.59),
"sra4" => array(0,0,637.80,907.09),
"letter" => array(0,0,612.00,792.00),
"legal" => array(0,0,612.00,1008.00),
"ledger" => array(0,0,1224.00, 792.00),
"tabloid" => array(0,0,792.00, 1224.00),
"executive" => array(0,0,521.86,756.00),
"folio" => array(0,0,612.00,936.00),
"commercial #10 envelope" => array(0,0,684,297),
"catalog #10 1/2 envelope" => array(0,0,648,864),
"8.5x11" => array(0,0,612.00,792.00),
"8.5x14" => array(0,0,612.00,1008.0),
"11x17" => array(0,0,792.00, 1224.00),
);
/**
* The DOMPDF object
*
* @var DOMPDF
*/
private $_dompdf;
/**
* Instance of Cpdf class
*
* @var Cpdf
*/
private $_pdf;
/**
* PDF width, in points
*
* @var float
*/
private $_width;
/**
* PDF height, in points
*
* @var float;
*/
private $_height;
/**
* Current page number
*
* @var int
*/
private $_page_number;
/**
* Total number of pages
*
* @var int
*/
private $_page_count;
/**
* Text to display on every page
*
* @var array
*/
private $_page_text;
/**
* Array of pages for accesing after rendering is initially complete
*
* @var array
*/
private $_pages;
/**
* Array of temporary cached images to be deleted when processing is complete
*
* @var array
*/
private $_image_cache;
/**
* Class constructor
*
* @param mixed $paper The size of paper to use in this PDF ({@link CPDF_Adapter::$PAPER_SIZES})
* @param string $orientation The orientation of the document (either 'landscape' or 'portrait')
* @param DOMPDF $dompdf The DOMPDF instance
*/
function __construct($paper = "letter", $orientation = "portrait", DOMPDF $dompdf) {
if ( is_array($paper) ) {
$size = $paper;
}
else if ( isset(self::$PAPER_SIZES[mb_strtolower($paper)]) ) {
$size = self::$PAPER_SIZES[mb_strtolower($paper)];
}
else {
$size = self::$PAPER_SIZES["letter"];
}
if ( mb_strtolower($orientation) === "landscape" ) {
list($size[2], $size[3]) = array($size[3], $size[2]);
}
$this->_dompdf = $dompdf;
$this->_pdf = new Cpdf(
$size,
$dompdf->get_option("enable_unicode"),
$dompdf->get_option("font_cache"),
$dompdf->get_option("temp_dir")
);
$this->_pdf->addInfo("Creator", "DOMPDF");
$time = substr_replace(date('YmdHisO'), '\'', -2, 0).'\'';
$this->_pdf->addInfo("CreationDate", "D:$time");
$this->_pdf->addInfo("ModDate", "D:$time");
$this->_width = $size[2] - $size[0];
$this->_height= $size[3] - $size[1];
$this->_page_number = $this->_page_count = 1;
$this->_page_text = array();
$this->_pages = array($this->_pdf->getFirstPageId());
$this->_image_cache = array();
}
function get_dompdf(){
return $this->_dompdf;
}
/**
* Class destructor
*
* Deletes all temporary image files
*/
function __destruct() {
foreach ($this->_image_cache as $img) {
// The file might be already deleted by 3rd party tmp cleaner,
// the file might not have been created at all
// (if image outputting commands failed)
// or because the destructor was called twice accidentally.
if (!file_exists($img)) {
continue;
}
if (DEBUGPNG) print '[__destruct unlink '.$img.']';
if (!DEBUGKEEPTEMP) unlink($img);
}
}
/**
* Returns the Cpdf instance
*
* @return Cpdf
*/
function get_cpdf() {
return $this->_pdf;
}
/**
* Add meta information to the PDF
*
* @param string $label label of the value (Creator, Producer, etc.)
* @param string $value the text to set
*/
function add_info($label, $value) {
$this->_pdf->addInfo($label, $value);
}
/**
* Opens a new 'object'
*
* While an object is open, all drawing actions are recored in the object,
* as opposed to being drawn on the current page. Objects can be added
* later to a specific page or to several pages.
*
* The return value is an integer ID for the new object.
*
* @see CPDF_Adapter::close_object()
* @see CPDF_Adapter::add_object()
*
* @return int
*/
function open_object() {
$ret = $this->_pdf->openObject();
$this->_pdf->saveState();
return $ret;
}
/**
* Reopens an existing 'object'
*
* @see CPDF_Adapter::open_object()
* @param int $object the ID of a previously opened object
*/
function reopen_object($object) {
$this->_pdf->reopenObject($object);
$this->_pdf->saveState();
}
/**
* Closes the current 'object'
*
* @see CPDF_Adapter::open_object()
*/
function close_object() {
$this->_pdf->restoreState();
$this->_pdf->closeObject();
}
/**
* Adds a specified 'object' to the document
*
* $object int specifying an object created with {@link
* CPDF_Adapter::open_object()}. $where can be one of:
* - 'add' add to current page only
* - 'all' add to every page from the current one onwards
* - 'odd' add to all odd numbered pages from now on
* - 'even' add to all even numbered pages from now on
* - 'next' add the object to the next page only
* - 'nextodd' add to all odd numbered pages from the next one
* - 'nexteven' add to all even numbered pages from the next one
*
* @see Cpdf::addObject()
*
* @param int $object
* @param string $where
*/
function add_object($object, $where = 'all') {
$this->_pdf->addObject($object, $where);
}
/**
* Stops the specified 'object' from appearing in the document.
*
* The object will stop being displayed on the page following the current
* one.
*
* @param int $object
*/
function stop_object($object) {
$this->_pdf->stopObject($object);
}
/**
* @access private
*/
function serialize_object($id) {
// Serialize the pdf object's current state for retrieval later
return $this->_pdf->serializeObject($id);
}
/**
* @access private
*/
function reopen_serialized_object($obj) {
return $this->_pdf->restoreSerializedObject($obj);
}
//........................................................................
/**
* Returns the PDF's width in points
* @return float
*/
function get_width() { return $this->_width; }
/**
* Returns the PDF's height in points
* @return float
*/
function get_height() { return $this->_height; }
/**
* Returns the current page number
* @return int
*/
function get_page_number() { return $this->_page_number; }
/**
* Returns the total number of pages in the document
* @return int
*/
function get_page_count() { return $this->_page_count; }
/**
* Sets the current page number
*
* @param int $num
*/
function set_page_number($num) { $this->_page_number = $num; }
/**
* Sets the page count
*
* @param int $count
*/
function set_page_count($count) { $this->_page_count = $count; }
/**
* Sets the stroke color
*
* See {@link Style::set_color()} for the format of the color array.
* @param array $color
*/
protected function _set_stroke_color($color) {
$this->_pdf->setStrokeColor($color);
}
/**
* Sets the fill colour
*
* See {@link Style::set_color()} for the format of the colour array.
* @param array $color
*/
protected function _set_fill_color($color) {
$this->_pdf->setColor($color);
}
/**
* Sets line transparency
* @see Cpdf::setLineTransparency()
*
* Valid blend modes are (case-sensitive):
*
* Normal, Multiply, Screen, Overlay, Darken, Lighten,
* ColorDodge, ColorBurn, HardLight, SoftLight, Difference,
* Exclusion
*
* @param string $mode the blending mode to use
* @param float $opacity 0.0 fully transparent, 1.0 fully opaque
*/
protected function _set_line_transparency($mode, $opacity) {
$this->_pdf->setLineTransparency($mode, $opacity);
}
/**
* Sets fill transparency
* @see Cpdf::setFillTransparency()
*
* Valid blend modes are (case-sensitive):
*
* Normal, Multiply, Screen, Overlay, Darken, Lighten,
* ColorDogde, ColorBurn, HardLight, SoftLight, Difference,
* Exclusion
*
* @param string $mode the blending mode to use
* @param float $opacity 0.0 fully transparent, 1.0 fully opaque
*/
protected function _set_fill_transparency($mode, $opacity) {
$this->_pdf->setFillTransparency($mode, $opacity);
}
/**
* Sets the line style
*
* @see Cpdf::setLineStyle()
*
* @param float $width
* @param string $cap
* @param string $join
* @param array $dash
*/
protected function _set_line_style($width, $cap, $join, $dash) {
$this->_pdf->setLineStyle($width, $cap, $join, $dash);
}
/**
* Sets the opacity
*
* @param $opacity
* @param $mode
*/
function set_opacity($opacity, $mode = "Normal") {
$this->_set_line_transparency($mode, $opacity);
$this->_set_fill_transparency($mode, $opacity);
}
function set_default_view($view, $options = array()) {
array_unshift($options, $view);
call_user_func_array(array($this->_pdf, "openHere"), $options);
}
/**
* Remaps y coords from 4th to 1st quadrant
*
* @param float $y
* @return float
*/
protected function y($y) {
return $this->_height - $y;
}
// Canvas implementation
function line($x1, $y1, $x2, $y2, $color, $width, $style = array()) {
$this->_set_stroke_color($color);
$this->_set_line_style($width, "butt", "", $style);
$this->_pdf->line($x1, $this->y($y1),
$x2, $this->y($y2));
}
function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = array()) {
$this->_set_stroke_color($color);
$this->_set_line_style($width, "butt", "", $style);
$this->_pdf->ellipse($x, $this->y($y), $r1, $r2, 0, 8, $astart, $aend, false, false, true, false);
}
//........................................................................
/**
* Convert a GIF or BMP image to a PNG image
*
* @param string $image_url
* @param integer $type
*
* @throws DOMPDF_Exception
* @return string The url of the newly converted image
*/
protected function _convert_gif_bmp_to_png($image_url, $type) {
$image_type = Image_Cache::type_to_ext($type);
$func_name = "imagecreatefrom$image_type";
if ( !function_exists($func_name) ) {
throw new DOMPDF_Exception("Function $func_name() not found. Cannot convert $image_type image: $image_url. Please install the image PHP extension.");
}
set_error_handler("record_warnings");
$im = $func_name($image_url);
if ( $im ) {
imageinterlace($im, false);
$tmp_dir = $this->_dompdf->get_option("temp_dir");
$tmp_name = tempnam($tmp_dir, "{$image_type}dompdf_img_");
@unlink($tmp_name);
$filename = "$tmp_name.png";
$this->_image_cache[] = $filename;
imagepng($im, $filename);
imagedestroy($im);
}
else {
$filename = Image_Cache::$broken_image;
}
restore_error_handler();
return $filename;
}
function rectangle($x1, $y1, $w, $h, $color, $width, $style = array()) {
$this->_set_stroke_color($color);
$this->_set_line_style($width, "butt", "", $style);
$this->_pdf->rectangle($x1, $this->y($y1) - $h, $w, $h);
}
function filled_rectangle($x1, $y1, $w, $h, $color) {
$this->_set_fill_color($color);
$this->_pdf->filledRectangle($x1, $this->y($y1) - $h, $w, $h);
}
function clipping_rectangle($x1, $y1, $w, $h) {
$this->_pdf->clippingRectangle($x1, $this->y($y1) - $h, $w, $h);
}
function clipping_roundrectangle($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL) {
$this->_pdf->clippingRectangleRounded($x1, $this->y($y1) - $h, $w, $h, $rTL, $rTR, $rBR, $rBL);
}
function clipping_end() {
$this->_pdf->clippingEnd();
}
function save() {
$this->_pdf->saveState();
}
function restore() {
$this->_pdf->restoreState();
}
function rotate($angle, $x, $y) {
$this->_pdf->rotate($angle, $x, $y);
}
function skew($angle_x, $angle_y, $x, $y) {
$this->_pdf->skew($angle_x, $angle_y, $x, $y);
}
function scale($s_x, $s_y, $x, $y) {
$this->_pdf->scale($s_x, $s_y, $x, $y);
}
function translate($t_x, $t_y) {
$this->_pdf->translate($t_x, $t_y);
}
function transform($a, $b, $c, $d, $e, $f) {
$this->_pdf->transform(array($a, $b, $c, $d, $e, $f));
}
function polygon($points, $color, $width = null, $style = array(), $fill = false) {
$this->_set_fill_color($color);
$this->_set_stroke_color($color);
// Adjust y values
for ( $i = 1; $i < count($points); $i += 2) {
$points[$i] = $this->y($points[$i]);
}
$this->_pdf->polygon($points, count($points) / 2, $fill);
}
function circle($x, $y, $r1, $color, $width = null, $style = null, $fill = false) {
$this->_set_fill_color($color);
$this->_set_stroke_color($color);
if ( !$fill && isset($width) ) {
$this->_set_line_style($width, "round", "round", $style);
}
$this->_pdf->ellipse($x, $this->y($y), $r1, 0, 0, 8, 0, 360, 1, $fill);
}
function image($img, $x, $y, $w, $h, $resolution = "normal") {
list($width, $height, $type) = dompdf_getimagesize($img);
$debug_png = $this->_dompdf->get_option("debug_png");
if ($debug_png) print "[image:$img|$width|$height|$type]";
switch ($type) {
case IMAGETYPE_JPEG:
if ($debug_png) print '!!!jpg!!!';
$this->_pdf->addJpegFromFile($img, $x, $this->y($y) - $h, $w, $h);
break;
case IMAGETYPE_GIF:
case IMAGETYPE_BMP:
if ($debug_png) print '!!!bmp or gif!!!';
// @todo use cache for BMP and GIF
$img = $this->_convert_gif_bmp_to_png($img, $type);
case IMAGETYPE_PNG:
if ($debug_png) print '!!!png!!!';
$this->_pdf->addPngFromFile($img, $x, $this->y($y) - $h, $w, $h);
break;
default:
if ($debug_png) print '!!!unknown!!!';
}
}
function text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0) {
$pdf = $this->_pdf;
$pdf->setColor($color);
$font .= ".afm";
$pdf->selectFont($font);
//Font_Metrics::get_font_height($font, $size) ==
//$this->get_font_height($font, $size) ==
//$this->_pdf->selectFont($font),$this->_pdf->getFontHeight($size)
//- FontBBoxheight+FontHeightOffset, scaled to $size, in pt
//$this->_pdf->getFontDescender($size)
//- Descender scaled to size
//
//$this->_pdf->fonts[$this->_pdf->currentFont] sizes:
//['FontBBox'][0] left, ['FontBBox'][1] bottom, ['FontBBox'][2] right, ['FontBBox'][3] top
//Maximum extent of all glyphs of the font from the baseline point
//['Ascender'] maximum height above baseline except accents
//['Descender'] maximum depth below baseline, negative number means below baseline
//['FontHeightOffset'] manual enhancement of .afm files to trim windows fonts. currently not used.
//Values are in 1/1000 pt for a font size of 1 pt
//
//['FontBBox'][1] should be close to ['Descender']
//['FontBBox'][3] should be close to ['Ascender']+Accents
//in practice, FontBBox values are a little bigger
//
//The text position is referenced to the baseline, not to the lower corner of the FontBBox,
//for what the left,top corner is given.
//FontBBox spans also the background box for the text.
//If the lower corner would be used as reference point, the Descents of the glyphs would
//hang over the background box border.
//Therefore compensate only the extent above the Baseline.
//
//print '<pre>['.$font.','.$size.','.$pdf->getFontHeight($size).','.$pdf->getFontDescender($size).','.$pdf->fonts[$pdf->currentFont]['FontBBox'][3].','.$pdf->fonts[$pdf->currentFont]['FontBBox'][1].','.$pdf->fonts[$pdf->currentFont]['FontHeightOffset'].','.$pdf->fonts[$pdf->currentFont]['Ascender'].','.$pdf->fonts[$pdf->currentFont]['Descender'].']</pre>';
//
//$pdf->addText($x, $this->y($y) - ($pdf->fonts[$pdf->currentFont]['FontBBox'][3]*$size)/1000, $size, $text, $angle, $word_space, $char_space);
$pdf->addText($x, $this->y($y) - $pdf->getFontHeight($size), $size, $text, $angle, $word_space, $char_space);
}
//........................................................................
function javascript($code) {
$this->_pdf->addJavascript($code);
}
//........................................................................
/**
* Add a named destination (similar to <a name="foo">...</a> in html)
*
* @param string $anchorname The name of the named destination
*/
function add_named_dest($anchorname) {
$this->_pdf->addDestination($anchorname, "Fit");
}
//........................................................................
/**
* Add a link to the pdf
*
* @param string $url The url to link to
* @param float $x The x position of the link
* @param float $y The y position of the link
* @param float $width The width of the link
* @param float $height The height of the link
*/
function add_link($url, $x, $y, $width, $height) {
$y = $this->y($y) - $height;
if ( strpos($url, '#') === 0 ) {
// Local link
$name = substr($url,1);
if ( $name ) {
$this->_pdf->addInternalLink($name, $x, $y, $x + $width, $y + $height);
}
}
else {
$this->_pdf->addLink(rawurldecode($url), $x, $y, $x + $width, $y + $height);
}
}
function get_text_width($text, $font, $size, $word_spacing = 0, $char_spacing = 0) {
$this->_pdf->selectFont($font);
$unicode = $this->_dompdf->get_option("enable_unicode");
if (!$unicode) {
$text = mb_convert_encoding($text, 'Windows-1252', 'UTF-8');
}
return $this->_pdf->getTextWidth($size, $text, $word_spacing, $char_spacing);
}
function register_string_subset($font, $string) {
$this->_pdf->registerText($font, $string);
}
function get_font_height($font, $size) {
$this->_pdf->selectFont($font);
$ratio = $this->_dompdf->get_option("font_height_ratio");
return $this->_pdf->getFontHeight($size) * $ratio;
}
/*function get_font_x_height($font, $size) {
$this->_pdf->selectFont($font);
$ratio = $this->_dompdf->get_option("font_height_ratio");
return $this->_pdf->getFontXHeight($size) * $ratio;
}*/
function get_font_baseline($font, $size) {
$ratio = $this->_dompdf->get_option("font_height_ratio");
return $this->get_font_height($font, $size) / $ratio;
}
/**
* Writes text at the specified x and y coordinates on every page
*
* The strings '{PAGE_NUM}' and '{PAGE_COUNT}' are automatically replaced
* with their current values.
*
* See {@link Style::munge_color()} for the format of the colour array.
*
* @param float $x
* @param float $y
* @param string $text the text to write
* @param string $font the font file to use
* @param float $size the font size, in points
* @param array $color
* @param float $word_space word spacing adjustment
* @param float $char_space char spacing adjustment
* @param float $angle angle to write the text at, measured CW starting from the x-axis
*/
function page_text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0) {
$_t = "text";
$this->_page_text[] = compact("_t", "x", "y", "text", "font", "size", "color", "word_space", "char_space", "angle");
}
/**
* Processes a script on every page
*
* The variables $pdf, $PAGE_NUM, and $PAGE_COUNT are available.
*
* This function can be used to add page numbers to all pages
* after the first one, for example.
*
* @param string $code the script code
* @param string $type the language type for script
*/
function page_script($code, $type = "text/php") {
$_t = "script";
$this->_page_text[] = compact("_t", "code", "type");
}
function new_page() {
$this->_page_number++;
$this->_page_count++;
$ret = $this->_pdf->newPage();
$this->_pages[] = $ret;
return $ret;
}
/**
* Add text to each page after rendering is complete
*/
protected function _add_page_text() {
if ( !count($this->_page_text) ) {
return;
}
$page_number = 1;
$eval = null;
foreach ($this->_pages as $pid) {
$this->reopen_object($pid);
foreach ($this->_page_text as $pt) {
extract($pt);
switch ($_t) {
case "text":
$text = str_replace(array("{PAGE_NUM}","{PAGE_COUNT}"),
array($page_number, $this->_page_count), $text);
$this->text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle);
break;
case "script":
if ( !$eval ) {
$eval = new PHP_Evaluator($this);
}
$eval->evaluate($code, array('PAGE_NUM' => $page_number, 'PAGE_COUNT' => $this->_page_count));
break;
}
}
$this->close_object();
$page_number++;
}
}
/**
* Streams the PDF directly to the browser
*
* @param string $filename the name of the PDF file
* @param array $options associative array, 'Attachment' => 0 or 1, 'compress' => 1 or 0
*/
function stream($filename, $options = null) {
// Add page text
$this->_add_page_text();
$options["Content-Disposition"] = $filename;
$this->_pdf->stream($options);
}
/**
* Returns the PDF as a string
*
* @param array $options Output options
* @return string
*/
function output($options = null) {
$this->_add_page_text();
$debug = isset($options["compress"]) && $options["compress"] != 1;
return $this->_pdf->output($debug);
}
/**
* Returns logging messages generated by the Cpdf class
*
* @return string
*/
function get_messages() {
return $this->_pdf->messages;
}
}

View File

@@ -0,0 +1,287 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author Fabien Ménager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
class CSS_Color {
static $cssColorNames = array(
"aliceblue" => "F0F8FF",
"antiquewhite" => "FAEBD7",
"aqua" => "00FFFF",
"aquamarine" => "7FFFD4",
"azure" => "F0FFFF",
"beige" => "F5F5DC",
"bisque" => "FFE4C4",
"black" => "000000",
"blanchedalmond" => "FFEBCD",
"blue" => "0000FF",
"blueviolet" => "8A2BE2",
"brown" => "A52A2A",
"burlywood" => "DEB887",
"cadetblue" => "5F9EA0",
"chartreuse" => "7FFF00",
"chocolate" => "D2691E",
"coral" => "FF7F50",
"cornflowerblue" => "6495ED",
"cornsilk" => "FFF8DC",
"crimson" => "DC143C",
"cyan" => "00FFFF",
"darkblue" => "00008B",
"darkcyan" => "008B8B",
"darkgoldenrod" => "B8860B",
"darkgray" => "A9A9A9",
"darkgreen" => "006400",
"darkgrey" => "A9A9A9",
"darkkhaki" => "BDB76B",
"darkmagenta" => "8B008B",
"darkolivegreen" => "556B2F",
"darkorange" => "FF8C00",
"darkorchid" => "9932CC",
"darkred" => "8B0000",
"darksalmon" => "E9967A",
"darkseagreen" => "8FBC8F",
"darkslateblue" => "483D8B",
"darkslategray" => "2F4F4F",
"darkslategrey" => "2F4F4F",
"darkturquoise" => "00CED1",
"darkviolet" => "9400D3",
"deeppink" => "FF1493",
"deepskyblue" => "00BFFF",
"dimgray" => "696969",
"dimgrey" => "696969",
"dodgerblue" => "1E90FF",
"firebrick" => "B22222",
"floralwhite" => "FFFAF0",
"forestgreen" => "228B22",
"fuchsia" => "FF00FF",
"gainsboro" => "DCDCDC",
"ghostwhite" => "F8F8FF",
"gold" => "FFD700",
"goldenrod" => "DAA520",
"gray" => "808080",
"green" => "008000",
"greenyellow" => "ADFF2F",
"grey" => "808080",
"honeydew" => "F0FFF0",
"hotpink" => "FF69B4",
"indianred" => "CD5C5C",
"indigo" => "4B0082",
"ivory" => "FFFFF0",
"khaki" => "F0E68C",
"lavender" => "E6E6FA",
"lavenderblush" => "FFF0F5",
"lawngreen" => "7CFC00",
"lemonchiffon" => "FFFACD",
"lightblue" => "ADD8E6",
"lightcoral" => "F08080",
"lightcyan" => "E0FFFF",
"lightgoldenrodyellow" => "FAFAD2",
"lightgray" => "D3D3D3",
"lightgreen" => "90EE90",
"lightgrey" => "D3D3D3",
"lightpink" => "FFB6C1",
"lightsalmon" => "FFA07A",
"lightseagreen" => "20B2AA",
"lightskyblue" => "87CEFA",
"lightslategray" => "778899",
"lightslategrey" => "778899",
"lightsteelblue" => "B0C4DE",
"lightyellow" => "FFFFE0",
"lime" => "00FF00",
"limegreen" => "32CD32",
"linen" => "FAF0E6",
"magenta" => "FF00FF",
"maroon" => "800000",
"mediumaquamarine" => "66CDAA",
"mediumblue" => "0000CD",
"mediumorchid" => "BA55D3",
"mediumpurple" => "9370DB",
"mediumseagreen" => "3CB371",
"mediumslateblue" => "7B68EE",
"mediumspringgreen" => "00FA9A",
"mediumturquoise" => "48D1CC",
"mediumvioletred" => "C71585",
"midnightblue" => "191970",
"mintcream" => "F5FFFA",
"mistyrose" => "FFE4E1",
"moccasin" => "FFE4B5",
"navajowhite" => "FFDEAD",
"navy" => "000080",
"oldlace" => "FDF5E6",
"olive" => "808000",
"olivedrab" => "6B8E23",
"orange" => "FFA500",
"orangered" => "FF4500",
"orchid" => "DA70D6",
"palegoldenrod" => "EEE8AA",
"palegreen" => "98FB98",
"paleturquoise" => "AFEEEE",
"palevioletred" => "DB7093",
"papayawhip" => "FFEFD5",
"peachpuff" => "FFDAB9",
"peru" => "CD853F",
"pink" => "FFC0CB",
"plum" => "DDA0DD",
"powderblue" => "B0E0E6",
"purple" => "800080",
"red" => "FF0000",
"rosybrown" => "BC8F8F",
"royalblue" => "4169E1",
"saddlebrown" => "8B4513",
"salmon" => "FA8072",
"sandybrown" => "F4A460",
"seagreen" => "2E8B57",
"seashell" => "FFF5EE",
"sienna" => "A0522D",
"silver" => "C0C0C0",
"skyblue" => "87CEEB",
"slateblue" => "6A5ACD",
"slategray" => "708090",
"slategrey" => "708090",
"snow" => "FFFAFA",
"springgreen" => "00FF7F",
"steelblue" => "4682B4",
"tan" => "D2B48C",
"teal" => "008080",
"thistle" => "D8BFD8",
"tomato" => "FF6347",
"turquoise" => "40E0D0",
"violet" => "EE82EE",
"wheat" => "F5DEB3",
"white" => "FFFFFF",
"whitesmoke" => "F5F5F5",
"yellow" => "FFFF00",
"yellowgreen" => "9ACD32",
);
static function parse($color) {
if ( is_array($color) ) {
// Assume the array has the right format...
// FIXME: should/could verify this.
return $color;
}
static $cache = array();
$color = strtolower($color);
if ( isset($cache[$color]) ) {
return $cache[$color];
}
if ( in_array($color, array("transparent", "inherit")) ) {
return $cache[$color] = $color;
}
if ( isset(self::$cssColorNames[$color]) ) {
return $cache[$color] = self::getArray(self::$cssColorNames[$color]);
}
$length = mb_strlen($color);
// #rgb format
if ( $length == 4 && $color[0] === "#" ) {
return $cache[$color] = self::getArray($color[1].$color[1].$color[2].$color[2].$color[3].$color[3]);
}
// #rrggbb format
else if ( $length == 7 && $color[0] === "#" ) {
return $cache[$color] = self::getArray(mb_substr($color, 1, 6));
}
// rgb( r,g,b ) / rgbaa( r,g,b,α ) format
else if ( mb_strpos($color, "rgb") !== false ) {
$i = mb_strpos($color, "(");
$j = mb_strpos($color, ")");
// Bad color value
if ( $i === false || $j === false ) {
return null;
}
$triplet = explode(",", mb_substr($color, $i+1, $j-$i-1));
// alpha transparency
// FIXME: not currently using transparency
$alpha = 1;
if ( count( $triplet ) == 4 ) {
$alpha = (float) ( trim( array_pop( $triplet ) ) );
// bad value, set to fully opaque
if ( $alpha > 1 || $alpha < 0 ) {
$alpha = 1;
}
}
if ( count($triplet) != 3 ) {
return null;
}
foreach (array_keys($triplet) as $c) {
$triplet[$c] = trim($triplet[$c]);
if ( $triplet[$c][mb_strlen($triplet[$c]) - 1] === "%" ) {
$triplet[$c] = round($triplet[$c] * 2.55);
}
}
return $cache[$color] = self::getArray(vsprintf("%02X%02X%02X", $triplet));
}
// cmyk( c,m,y,k ) format
// http://www.w3.org/TR/css3-gcpm/#cmyk-colors
else if ( mb_strpos($color, "cmyk") !== false ) {
$i = mb_strpos($color, "(");
$j = mb_strpos($color, ")");
// Bad color value
if ( $i === false || $j === false ) {
return null;
}
$values = explode(",", mb_substr($color, $i+1, $j-$i-1));
if ( count($values) != 4 ) {
return null;
}
foreach ($values as &$c) {
$c = floatval(trim($c));
if ($c > 1.0) $c = 1.0;
if ($c < 0.0) $c = 0.0;
}
return $cache[$color] = self::getArray($values);
}
return null;
}
static function getArray($color) {
$c = array(null, null, null, null, "hex" => null);
if (is_array($color)) {
$c = $color;
$c["c"] = $c[0];
$c["m"] = $c[1];
$c["y"] = $c[2];
$c["k"] = $c[3];
$c["hex"] = "cmyk($c[0],$c[1],$c[2],$c[3])";
}
else {
$c[0] = hexdec(mb_substr($color, 0, 2)) / 0xff;
$c[1] = hexdec(mb_substr($color, 2, 2)) / 0xff;
$c[2] = hexdec(mb_substr($color, 4, 2)) / 0xff;
$c["r"] = $c[0];
$c["g"] = $c[1];
$c["b"] = $c[2];
$c["hex"] = "#$color";
}
return $c;
}
}

View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,26 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Standard exception thrown by DOMPDF classes
*
* @package dompdf
*/
class DOMPDF_Exception extends Exception {
/**
* Class constructor
*
* @param string $message Error message
* @param int $code Error code
*/
function __construct($message = null, $code = 0) {
parent::__construct($message, $code);
}
}

View File

@@ -0,0 +1,26 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Image exception thrown by DOMPDF
*
* @package dompdf
*/
class DOMPDF_Image_Exception extends DOMPDF_Exception {
/**
* Class constructor
*
* @param string $message Error message
* @param int $code Error code
*/
function __construct($message = null, $code = 0) {
parent::__construct($message, $code);
}
}

View File

@@ -0,0 +1,8 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author ...
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/

View File

@@ -0,0 +1,88 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author Fabien Ménager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Positions fixely positioned frames
*/
class Fixed_Positioner extends Positioner {
function __construct(Frame_Decorator $frame) { parent::__construct($frame); }
function position() {
$frame = $this->_frame;
$style = $frame->get_original_style();
$root = $frame->get_root();
$initialcb = $root->get_containing_block();
$initialcb_style = $root->get_style();
$p = $frame->find_block_parent();
if ( $p ) {
$p->add_line();
}
// Compute the margins of the @page style
$margin_top = $initialcb_style->length_in_pt($initialcb_style->margin_top, $initialcb["h"]);
$margin_right = $initialcb_style->length_in_pt($initialcb_style->margin_right, $initialcb["w"]);
$margin_bottom = $initialcb_style->length_in_pt($initialcb_style->margin_bottom, $initialcb["h"]);
$margin_left = $initialcb_style->length_in_pt($initialcb_style->margin_left, $initialcb["w"]);
// The needed computed style of the element
$height = $style->length_in_pt($style->height, $initialcb["h"]);
$width = $style->length_in_pt($style->width, $initialcb["w"]);
$top = $style->length_in_pt($style->top, $initialcb["h"]);
$right = $style->length_in_pt($style->right, $initialcb["w"]);
$bottom = $style->length_in_pt($style->bottom, $initialcb["h"]);
$left = $style->length_in_pt($style->left, $initialcb["w"]);
$y = $margin_top;
if ( isset($top) ) {
$y = $top + $margin_top;
if ( $top === "auto" ) {
$y = $margin_top;
if ( isset($bottom) && $bottom !== "auto" ) {
$y = $initialcb["h"] - $bottom - $margin_bottom;
$margin_height = $this->_frame->get_margin_height();
if ( $margin_height !== "auto" ) {
$y -= $margin_height;
}
else {
$y -= $height;
}
}
}
}
$x = $margin_left;
if ( isset($left) ) {
$x = $left + $margin_left;
if ( $left === "auto" ) {
$x = $margin_left;
if ( isset($right) && $right !== "auto" ) {
$x = $initialcb["w"] - $right - $margin_right;
$margin_width = $this->_frame->get_margin_width();
if ( $margin_width !== "auto" ) {
$x -= $margin_width;
}
else {
$x -= $width;
}
}
}
}
$frame->set_position($x, $y);
$children = $frame->get_children();
foreach($children as $child) {
$child->set_position($x, $y);
}
}
}

View File

@@ -0,0 +1,363 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author Helmut Tischer <htischer@weihenstephan.org>
* @author Fabien Ménager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
require_once DOMPDF_LIB_DIR . "/class.pdf.php";
/**
* Name of the font cache file
*
* This file must be writable by the webserver process only to update it
* with save_font_families() after adding the .afm file references of a new font family
* with Font_Metrics::save_font_families().
* This is typically done only from command line with load_font.php on converting
* ttf fonts to ufm with php-font-lib.
*
* Declared here because PHP5 prevents constants from being declared with expressions
*/
define('__DOMPDF_FONT_CACHE_FILE', DOMPDF_FONT_DIR . "dompdf_font_family_cache.php");
/**
* The font metrics class
*
* This class provides information about fonts and text. It can resolve
* font names into actual installed font files, as well as determine the
* size of text in a particular font and size.
*
* @static
* @package dompdf
*/
class Font_Metrics {
/**
* @see __DOMPDF_FONT_CACHE_FILE
*/
const CACHE_FILE = __DOMPDF_FONT_CACHE_FILE;
/**
* Underlying {@link Canvas} object to perform text size calculations
*
* @var Canvas
*/
static protected $_pdf = null;
/**
* Array of font family names to font files
*
* Usually cached by the {@link load_font.php} script
*
* @var array
*/
static protected $_font_lookup = array();
/**
* Class initialization
*
*/
static function init(Canvas $canvas = null) {
if (!self::$_pdf) {
if (!$canvas) {
$canvas = Canvas_Factory::get_instance(new DOMPDF());
}
self::$_pdf = $canvas;
}
}
/**
* Calculates text size, in points
*
* @param string $text the text to be sized
* @param string $font the desired font
* @param float $size the desired font size
* @param float $word_spacing
* @param float $char_spacing
*
* @internal param float $spacing word spacing, if any
* @return float
*/
static function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0) {
//return self::$_pdf->get_text_width($text, $font, $size, $word_spacing, $char_spacing);
// @todo Make sure this cache is efficient before enabling it
static $cache = array();
if ( $text === "" ) {
return 0;
}
// Don't cache long strings
$use_cache = !isset($text[50]); // Faster than strlen
$key = "$font/$size/$word_spacing/$char_spacing";
if ( $use_cache && isset($cache[$key][$text]) ) {
return $cache[$key]["$text"];
}
$width = self::$_pdf->get_text_width($text, $font, $size, $word_spacing, $char_spacing);
if ( $use_cache ) {
$cache[$key][$text] = $width;
}
return $width;
}
/**
* Calculates font height
*
* @param string $font
* @param float $size
* @return float
*/
static function get_font_height($font, $size) {
return self::$_pdf->get_font_height($font, $size);
}
/**
* Resolves a font family & subtype into an actual font file
* Subtype can be one of 'normal', 'bold', 'italic' or 'bold_italic'. If
* the particular font family has no suitable font file, the default font
* ({@link DOMPDF_DEFAULT_FONT}) is used. The font file returned
* is the absolute pathname to the font file on the system.
*
* @param string $family_raw
* @param string $subtype_raw
*
* @return string
*/
static function get_font($family_raw, $subtype_raw = "normal") {
static $cache = array();
if ( isset($cache[$family_raw][$subtype_raw]) ) {
return $cache[$family_raw][$subtype_raw];
}
/* Allow calling for various fonts in search path. Therefore not immediately
* return replacement on non match.
* Only when called with NULL try replacement.
* When this is also missing there is really trouble.
* If only the subtype fails, nevertheless return failure.
* Only on checking the fallback font, check various subtypes on same font.
*/
$subtype = strtolower($subtype_raw);
if ( $family_raw ) {
$family = str_replace( array("'", '"'), "", strtolower($family_raw));
if ( isset(self::$_font_lookup[$family][$subtype]) ) {
return $cache[$family_raw][$subtype_raw] = self::$_font_lookup[$family][$subtype];
}
return null;
}
$family = "serif";
if ( isset(self::$_font_lookup[$family][$subtype]) ) {
return $cache[$family_raw][$subtype_raw] = self::$_font_lookup[$family][$subtype];
}
if ( !isset(self::$_font_lookup[$family]) ) {
return null;
}
$family = self::$_font_lookup[$family];
foreach ( $family as $sub => $font ) {
if (strpos($subtype, $sub) !== false) {
return $cache[$family_raw][$subtype_raw] = $font;
}
}
if ($subtype !== "normal") {
foreach ( $family as $sub => $font ) {
if ($sub !== "normal") {
return $cache[$family_raw][$subtype_raw] = $font;
}
}
}
$subtype = "normal";
if ( isset($family[$subtype]) ) {
return $cache[$family_raw][$subtype_raw] = $family[$subtype];
}
return null;
}
static function get_family($family) {
$family = str_replace( array("'", '"'), "", mb_strtolower($family));
if ( isset(self::$_font_lookup[$family]) ) {
return self::$_font_lookup[$family];
}
return null;
}
/**
* Saves the stored font family cache
*
* The name and location of the cache file are determined by {@link
* Font_Metrics::CACHE_FILE}. This file should be writable by the
* webserver process.
*
* @see Font_Metrics::load_font_families()
*/
static function save_font_families() {
// replace the path to the DOMPDF font directories with the corresponding constants (allows for more portability)
$cache_data = var_export(self::$_font_lookup, true);
$cache_data = str_replace('\''.DOMPDF_FONT_DIR , 'DOMPDF_FONT_DIR . \'' , $cache_data);
$cache_data = str_replace('\''.DOMPDF_DIR , 'DOMPDF_DIR . \'' , $cache_data);
$cache_data = "<"."?php return $cache_data ?".">";
file_put_contents(self::CACHE_FILE, $cache_data);
}
/**
* Loads the stored font family cache
*
* @see save_font_families()
*/
static function load_font_families() {
$dist_fonts = require_once DOMPDF_DIR . "/lib/fonts/dompdf_font_family_cache.dist.php";
// FIXME: temporary step for font cache created before the font cache fix
if ( is_readable( DOMPDF_FONT_DIR . "dompdf_font_family_cache" ) ) {
$old_fonts = require_once DOMPDF_FONT_DIR . "dompdf_font_family_cache";
// If the font family cache is still in the old format
if ( $old_fonts === 1 ) {
$cache_data = file_get_contents(DOMPDF_FONT_DIR . "dompdf_font_family_cache");
file_put_contents(DOMPDF_FONT_DIR . "dompdf_font_family_cache", "<"."?php return $cache_data ?".">");
$old_fonts = require_once DOMPDF_FONT_DIR . "dompdf_font_family_cache";
}
$dist_fonts += $old_fonts;
}
if ( !is_readable(self::CACHE_FILE) ) {
self::$_font_lookup = $dist_fonts;
return;
}
self::$_font_lookup = require_once self::CACHE_FILE;
// If the font family cache is still in the old format
if ( self::$_font_lookup === 1 ) {
$cache_data = file_get_contents(self::CACHE_FILE);
file_put_contents(self::CACHE_FILE, "<"."?php return $cache_data ?".">");
self::$_font_lookup = require_once self::CACHE_FILE;
}
// Merge provided fonts
self::$_font_lookup += $dist_fonts;
}
static function get_type($type) {
if (preg_match("/bold/i", $type)) {
if (preg_match("/italic|oblique/i", $type)) {
$type = "bold_italic";
}
else {
$type = "bold";
}
}
elseif (preg_match("/italic|oblique/i", $type)) {
$type = "italic";
}
else {
$type = "normal";
}
return $type;
}
static function install_fonts($files) {
$names = array();
foreach($files as $file) {
$font = Font::load($file);
$records = $font->getData("name", "records");
$type = self::get_type($records[2]);
$names[mb_strtolower($records[1])][$type] = $file;
}
return $names;
}
static function get_system_fonts() {
$files = glob("/usr/share/fonts/truetype/*.ttf") +
glob("/usr/share/fonts/truetype/*/*.ttf") +
glob("/usr/share/fonts/truetype/*/*/*.ttf") +
glob("C:\\Windows\\fonts\\*.ttf") +
glob("C:\\WinNT\\fonts\\*.ttf") +
glob("/mnt/c_drive/WINDOWS/Fonts/");
return self::install_fonts($files);
}
/**
* Returns the current font lookup table
*
* @return array
*/
static function get_font_families() {
return self::$_font_lookup;
}
static function set_font_family($fontname, $entry) {
self::$_font_lookup[mb_strtolower($fontname)] = $entry;
}
static function register_font($style, $remote_file) {
$fontname = mb_strtolower($style["family"]);
$families = Font_Metrics::get_font_families();
$entry = array();
if ( isset($families[$fontname]) ) {
$entry = $families[$fontname];
}
$local_file = DOMPDF_FONT_DIR . md5($remote_file);
$cache_entry = $local_file;
$local_file .= ".ttf";
$style_string = Font_Metrics::get_type("{$style['weight']} {$style['style']}");
if ( !isset($entry[$style_string]) ) {
$entry[$style_string] = $cache_entry;
Font_Metrics::set_font_family($fontname, $entry);
// Download the remote file
if ( !is_file($local_file) ) {
file_put_contents($local_file, file_get_contents($remote_file));
}
$font = Font::load($local_file);
if (!$font) {
return false;
}
$font->parse();
$font->saveAdobeFontMetrics("$cache_entry.ufm");
// Save the changes
Font_Metrics::save_font_families();
}
return true;
}
}
Font_Metrics::load_font_families();

View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,717 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Base Frame_Decorator class
*
* @access private
* @package dompdf
*/
abstract class Frame_Decorator extends Frame {
const DEFAULT_COUNTER = "-dompdf-default-counter";
public $_counters = array(); // array([id] => counter_value) (for generated content)
/**
* The root node of the DOM tree
*
* @var Frame
*/
protected $_root;
/**
* The decorated frame
*
* @var Frame
*/
protected $_frame;
/**
* Positioner object used to position this frame (Strategy pattern)
*
* @var Positioner
*/
protected $_positioner;
/**
* Reflower object used to calculate frame dimensions (Strategy pattern)
*
* @var Frame_Reflower
*/
protected $_reflower;
/**
* Reference to the current dompdf instance
*
* @var DOMPDF
*/
protected $_dompdf;
/**
* First block parent
*
* @var Block_Frame_Decorator
*/
private $_block_parent;
/**
* First positionned parent (position: relative | absolute | fixed)
*
* @var Frame_Decorator
*/
private $_positionned_parent;
/**
* Class constructor
*
* @param Frame $frame The decoration target
* @param DOMPDF $dompdf The DOMPDF object
*/
function __construct(Frame $frame, DOMPDF $dompdf) {
$this->_frame = $frame;
$this->_root = null;
$this->_dompdf = $dompdf;
$frame->set_decorator($this);
}
/**
* "Destructor": foribly free all references held by this object
*
* @param bool $recursive if true, call dispose on all children
*/
function dispose($recursive = false) {
if ( $recursive ) {
while ( $child = $this->get_first_child() ) {
$child->dispose(true);
}
}
$this->_root = null;
unset($this->_root);
$this->_frame->dispose(true);
$this->_frame = null;
unset($this->_frame);
$this->_positioner = null;
unset($this->_positioner);
$this->_reflower = null;
unset($this->_reflower);
}
/**
* Return a copy of this frame with $node as its node
*
* @param DOMNode $node
*
* @return Frame
*/
function copy(DOMNode $node) {
$frame = new Frame($node);
$frame->set_style(clone $this->_frame->get_original_style());
return Frame_Factory::decorate_frame($frame, $this->_dompdf, $this->_root);
}
/**
* Create a deep copy: copy this node and all children
*
* @return Frame
*/
function deep_copy() {
$frame = new Frame($this->get_node()->cloneNode());
$frame->set_style(clone $this->_frame->get_original_style());
$deco = Frame_Factory::decorate_frame($frame, $this->_dompdf, $this->_root);
foreach ($this->get_children() as $child) {
$deco->append_child($child->deep_copy());
}
return $deco;
}
/**
* Delegate calls to decorated frame object
*/
function reset() {
$this->_frame->reset();
$this->_counters = array();
// Reset all children
foreach ($this->get_children() as $child) {
$child->reset();
}
}
// Getters -----------
function get_id() {
return $this->_frame->get_id();
}
/**
* @return Frame
*/
function get_frame() {
return $this->_frame;
}
/**
* @return DOMElement|DOMText
*/
function get_node() {
return $this->_frame->get_node();
}
/**
* @return Style
*/
function get_style() {
return $this->_frame->get_style();
}
/**
* @return Style
*/
function get_original_style() {
return $this->_frame->get_original_style();
}
/**
* @param integer $i
*
* @return array|float
*/
function get_containing_block($i = null) {
return $this->_frame->get_containing_block($i);
}
/**
* @param integer $i
*
* @return array|float
*/
function get_position($i = null) {
return $this->_frame->get_position($i);
}
/**
* @return DOMPDF
*/
function get_dompdf() {
return $this->_dompdf;
}
/**
* @return float
*/
function get_margin_height() {
return $this->_frame->get_margin_height();
}
/**
* @return float
*/
function get_margin_width() {
return $this->_frame->get_margin_width();
}
/**
* @return array
*/
function get_padding_box() {
return $this->_frame->get_padding_box();
}
/**
* @return array
*/
function get_border_box() {
return $this->_frame->get_border_box();
}
/**
* @param integer $id
*/
function set_id($id) {
$this->_frame->set_id($id);
}
/**
* @param Style $style
*/
function set_style(Style $style) {
$this->_frame->set_style($style);
}
/**
* @param float $x
* @param float $y
* @param float $w
* @param float $h
*/
function set_containing_block($x = null, $y = null, $w = null, $h = null) {
$this->_frame->set_containing_block($x, $y, $w, $h);
}
/**
* @param float $x
* @param float $y
*/
function set_position($x = null, $y = null) {
$this->_frame->set_position($x, $y);
}
/**
* @return string
*/
function __toString() {
return $this->_frame->__toString();
}
/**
* @param Frame $child
* @param bool $update_node
*/
function prepend_child(Frame $child, $update_node = true) {
while ( $child instanceof Frame_Decorator ) {
$child = $child->_frame;
}
$this->_frame->prepend_child($child, $update_node);
}
/**
* @param Frame $child
* @param bool $update_node
*/
function append_child(Frame $child, $update_node = true) {
while ( $child instanceof Frame_Decorator ) {
$child = $child->_frame;
}
$this->_frame->append_child($child, $update_node);
}
/**
* @param Frame $new_child
* @param Frame $ref
* @param bool $update_node
*/
function insert_child_before(Frame $new_child, Frame $ref, $update_node = true) {
while ( $new_child instanceof Frame_Decorator ) {
$new_child = $new_child->_frame;
}
if ( $ref instanceof Frame_Decorator ) {
$ref = $ref->_frame;
}
$this->_frame->insert_child_before($new_child, $ref, $update_node);
}
/**
* @param Frame $new_child
* @param Frame $ref
* @param bool $update_node
*/
function insert_child_after(Frame $new_child, Frame $ref, $update_node = true) {
while ( $new_child instanceof Frame_Decorator ) {
$new_child = $new_child->_frame;
}
while ( $ref instanceof Frame_Decorator ) {
$ref = $ref->_frame;
}
$this->_frame->insert_child_after($new_child, $ref, $update_node);
}
/**
* @param Frame $child
* @param bool $update_node
*
* @return Frame
*/
function remove_child(Frame $child, $update_node = true) {
while ( $child instanceof Frame_Decorator ) {
$child = $child->_frame;
}
return $this->_frame->remove_child($child, $update_node);
}
/**
* @return Frame_Decorator
*/
function get_parent() {
$p = $this->_frame->get_parent();
if ( $p && $deco = $p->get_decorator() ) {
while ( $tmp = $deco->get_decorator() ) {
$deco = $tmp;
}
return $deco;
}
else if ( $p ) {
return $p;
}
return null;
}
/**
* @return Frame_Decorator
*/
function get_first_child() {
$c = $this->_frame->get_first_child();
if ( $c && $deco = $c->get_decorator() ) {
while ( $tmp = $deco->get_decorator() ) {
$deco = $tmp;
}
return $deco;
}
else if ( $c ) {
return $c;
}
return null;
}
/**
* @return Frame_Decorator
*/
function get_last_child() {
$c = $this->_frame->get_last_child();
if ( $c && $deco = $c->get_decorator() ) {
while ( $tmp = $deco->get_decorator() ) {
$deco = $tmp;
}
return $deco;
}
else if ( $c ) {
return $c;
}
return null;
}
/**
* @return Frame_Decorator
*/
function get_prev_sibling() {
$s = $this->_frame->get_prev_sibling();
if ( $s && $deco = $s->get_decorator() ) {
while ( $tmp = $deco->get_decorator() ) {
$deco = $tmp;
}
return $deco;
}
else if ( $s ) {
return $s;
}
return null;
}
/**
* @return Frame_Decorator
*/
function get_next_sibling() {
$s = $this->_frame->get_next_sibling();
if ( $s && $deco = $s->get_decorator() ) {
while ( $tmp = $deco->get_decorator() ) {
$deco = $tmp;
}
return $deco;
}
else if ( $s ) {
return $s;
}
return null;
}
/**
* @return FrameTreeList
*/
function get_subtree() {
return new FrameTreeList($this);
}
function set_positioner(Positioner $posn) {
$this->_positioner = $posn;
if ( $this->_frame instanceof Frame_Decorator ) {
$this->_frame->set_positioner($posn);
}
}
function set_reflower(Frame_Reflower $reflower) {
$this->_reflower = $reflower;
if ( $this->_frame instanceof Frame_Decorator ) {
$this->_frame->set_reflower( $reflower );
}
}
/**
* @return Frame_Reflower
*/
function get_reflower() {
return $this->_reflower;
}
/**
* @param Frame $root
*/
function set_root(Frame $root) {
$this->_root = $root;
if ( $this->_frame instanceof Frame_Decorator ) {
$this->_frame->set_root($root);
}
}
/**
* @return Page_Frame_Decorator
*/
function get_root() {
return $this->_root;
}
/**
* @return Block_Frame_Decorator
*/
function find_block_parent() {
// Find our nearest block level parent
$p = $this->get_parent();
while ( $p ) {
if ( $p->is_block() ) {
break;
}
$p = $p->get_parent();
}
return $this->_block_parent = $p;
}
/**
* @return Frame_Decorator
*/
function find_positionned_parent() {
// Find our nearest relative positionned parent
$p = $this->get_parent();
while ( $p ) {
if ( $p->is_positionned() ) {
break;
}
$p = $p->get_parent();
}
if ( !$p ) {
$p = $this->_root->get_first_child(); // <body>
}
return $this->_positionned_parent = $p;
}
/**
* split this frame at $child.
* The current frame is cloned and $child and all children following
* $child are added to the clone. The clone is then passed to the
* current frame's parent->split() method.
*
* @param Frame $child
* @param boolean $force_pagebreak
*
* @throws DOMPDF_Exception
* @return void
*/
function split(Frame $child = null, $force_pagebreak = false) {
// decrement any counters that were incremented on the current node, unless that node is the body
$style = $this->_frame->get_style();
if ( $this->_frame->get_node()->nodeName !== "body" && $style->counter_increment && ($decrement = $style->counter_increment) !== "none" ) {
$this->decrement_counters($decrement);
}
if ( is_null( $child ) ) {
// check for counter increment on :before content (always a child of the selected element @link Frame_Reflower::_set_content)
// this can push the current node to the next page before counter rules have bubbled up (but only if
// it's been rendered, thus the position check)
if ( !$this->is_text_node() && $this->get_node()->hasAttribute("dompdf_before_frame_id") ) {
foreach($this->_frame->get_children() as $child) {
if ( $this->get_node()->getAttribute("dompdf_before_frame_id") == $child->get_id() && $child->get_position('x') !== NULL ) {
$style = $child->get_style();
if ( $style->counter_increment && ($decrement = $style->counter_increment) !== "none" ) {
$this->decrement_counters($decrement);
}
}
}
}
$this->get_parent()->split($this, $force_pagebreak);
return;
}
if ( $child->get_parent() !== $this ) {
throw new DOMPDF_Exception("Unable to split: frame is not a child of this one.");
}
$node = $this->_frame->get_node();
$split = $this->copy( $node->cloneNode() );
$split->reset();
$split->get_original_style()->text_indent = 0;
$split->_splitted = true;
// The body's properties must be kept
if ( $node->nodeName !== "body" ) {
// Style reset on the first and second parts
$style = $this->_frame->get_style();
$style->margin_bottom = 0;
$style->padding_bottom = 0;
$style->border_bottom = 0;
// second
$orig_style = $split->get_original_style();
$orig_style->text_indent = 0;
$orig_style->margin_top = 0;
$orig_style->padding_top = 0;
$orig_style->border_top = 0;
}
$this->get_parent()->insert_child_after($split, $this);
// Add $frame and all following siblings to the new split node
$iter = $child;
while ($iter) {
$frame = $iter;
$iter = $iter->get_next_sibling();
$frame->reset();
$split->append_child($frame);
}
$this->get_parent()->split($split, $force_pagebreak);
// If this node resets a counter save the current value to use when rendering on the next page
if ( $style->counter_reset && ( $reset = $style->counter_reset ) !== "none" ) {
$vars = preg_split( '/\s+/' , trim( $reset ) , 2 );
$split->_counters[ '__' . $vars[0] ] = $this->lookup_counter_frame( $vars[0] )->_counters[$vars[0]];
}
}
function reset_counter($id = self::DEFAULT_COUNTER, $value = 0) {
$this->get_parent()->_counters[$id] = intval($value);
}
function decrement_counters($counters) {
foreach($counters as $id => $increment) {
$this->increment_counter($id, intval($increment) * -1);
}
}
function increment_counters($counters) {
foreach($counters as $id => $increment) {
$this->increment_counter($id, intval($increment));
}
}
function increment_counter($id = self::DEFAULT_COUNTER, $increment = 1) {
$counter_frame = $this->lookup_counter_frame($id);
if ( $counter_frame ) {
if ( !isset($counter_frame->_counters[$id]) ) {
$counter_frame->_counters[$id] = 0;
}
$counter_frame->_counters[$id] += $increment;
}
}
function lookup_counter_frame($id = self::DEFAULT_COUNTER) {
$f = $this->get_parent();
while( $f ) {
if( isset($f->_counters[$id]) ) {
return $f;
}
$fp = $f->get_parent();
if ( !$fp ) {
return $f;
}
$f = $fp;
}
}
// TODO: What version is the best : this one or the one in List_Bullet_Renderer ?
function counter_value($id = self::DEFAULT_COUNTER, $type = "decimal") {
$type = mb_strtolower($type);
if ( !isset($this->_counters[$id]) ) {
$this->_counters[$id] = 0;
}
$value = $this->_counters[$id];
switch ($type) {
default:
case "decimal":
return $value;
case "decimal-leading-zero":
return str_pad($value, 2, "0");
case "lower-roman":
return dec2roman($value);
case "upper-roman":
return mb_strtoupper(dec2roman($value));
case "lower-latin":
case "lower-alpha":
return chr( ($value % 26) + ord('a') - 1);
case "upper-latin":
case "upper-alpha":
return chr( ($value % 26) + ord('A') - 1);
case "lower-greek":
return unichr($value + 944);
case "upper-greek":
return unichr($value + 912);
}
}
final function position() {
$this->_positioner->position();
}
final function move($offset_x, $offset_y, $ignore_self = false) {
$this->_positioner->move($offset_x, $offset_y, $ignore_self);
}
final function reflow(Block_Frame_Decorator $block = null) {
// Uncomment this to see the frames before they're laid out, instead of
// during rendering.
//echo $this->_frame; flush();
$this->_reflower->reflow($block);
}
final function get_min_max_width() {
return $this->_reflower->get_min_max_width();
}
}

View File

@@ -0,0 +1,252 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Contains frame decorating logic
*
* This class is responsible for assigning the correct {@link Frame_Decorator},
* {@link Positioner}, and {@link Frame_Reflower} objects to {@link Frame}
* objects. This is determined primarily by the Frame's display type, but
* also by the Frame's node's type (e.g. DomElement vs. #text)
*
* @access private
* @package dompdf
*/
class Frame_Factory {
/**
* Decorate the root Frame
*
* @param $root Frame The frame to decorate
* @param $dompdf DOMPDF The dompdf instance
* @return Page_Frame_Decorator
*/
static function decorate_root(Frame $root, DOMPDF $dompdf) {
$frame = new Page_Frame_Decorator($root, $dompdf);
$frame->set_reflower( new Page_Frame_Reflower($frame) );
$root->set_decorator($frame);
return $frame;
}
/**
* Decorate a Frame
*
* @param Frame $frame The frame to decorate
* @param DOMPDF $dompdf The dompdf instance
* @param Frame $root The frame to decorate
*
* @throws DOMPDF_Exception
* @return Frame_Decorator
* FIXME: this is admittedly a little smelly...
*/
static function decorate_frame(Frame $frame, DOMPDF $dompdf, Frame $root = null) {
if ( is_null($dompdf) ) {
throw new DOMPDF_Exception("The DOMPDF argument is required");
}
$style = $frame->get_style();
// Floating (and more generally out-of-flow) elements are blocks
// http://coding.smashingmagazine.com/2007/05/01/css-float-theory-things-you-should-know/
if ( !$frame->is_in_flow() && in_array($style->display, Style::$INLINE_TYPES)) {
$style->display = "block";
}
$display = $style->display;
switch ($display) {
case "block":
$positioner = "Block";
$decorator = "Block";
$reflower = "Block";
break;
case "inline-block":
$positioner = "Inline";
$decorator = "Block";
$reflower = "Block";
break;
case "inline":
$positioner = "Inline";
if ( $frame->is_text_node() ) {
$decorator = "Text";
$reflower = "Text";
}
else {
$enable_css_float = $dompdf->get_option("enable_css_float");
if ( $enable_css_float && $style->float !== "none" ) {
$decorator = "Block";
$reflower = "Block";
}
else {
$decorator = "Inline";
$reflower = "Inline";
}
}
break;
case "table":
$positioner = "Block";
$decorator = "Table";
$reflower = "Table";
break;
case "inline-table":
$positioner = "Inline";
$decorator = "Table";
$reflower = "Table";
break;
case "table-row-group":
case "table-header-group":
case "table-footer-group":
$positioner = "Null";
$decorator = "Table_Row_Group";
$reflower = "Table_Row_Group";
break;
case "table-row":
$positioner = "Null";
$decorator = "Table_Row";
$reflower = "Table_Row";
break;
case "table-cell":
$positioner = "Table_Cell";
$decorator = "Table_Cell";
$reflower = "Table_Cell";
break;
case "list-item":
$positioner = "Block";
$decorator = "Block";
$reflower = "Block";
break;
case "-dompdf-list-bullet":
if ( $style->list_style_position === "inside" ) {
$positioner = "Inline";
}
else {
$positioner = "List_Bullet";
}
if ( $style->list_style_image !== "none" ) {
$decorator = "List_Bullet_Image";
}
else {
$decorator = "List_Bullet";
}
$reflower = "List_Bullet";
break;
case "-dompdf-image":
$positioner = "Inline";
$decorator = "Image";
$reflower = "Image";
break;
case "-dompdf-br":
$positioner = "Inline";
$decorator = "Inline";
$reflower = "Inline";
break;
default:
// FIXME: should throw some sort of warning or something?
case "none":
if ( $style->_dompdf_keep !== "yes" ) {
// Remove the node and the frame
$frame->get_parent()->remove_child($frame);
return;
}
$positioner = "Null";
$decorator = "Null";
$reflower = "Null";
break;
}
// Handle CSS position
$position = $style->position;
if ( $position === "absolute" ) {
$positioner = "Absolute";
}
else if ( $position === "fixed" ) {
$positioner = "Fixed";
}
$node = $frame->get_node();
// Handle nodeName
if ( $node->nodeName === "img" ) {
$style->display = "-dompdf-image";
$decorator = "Image";
$reflower = "Image";
}
$positioner .= "_Positioner";
$decorator .= "_Frame_Decorator";
$reflower .= "_Frame_Reflower";
$deco = new $decorator($frame, $dompdf);
$deco->set_positioner( new $positioner($deco) );
$deco->set_reflower( new $reflower($deco) );
if ( $root ) {
$deco->set_root($root);
}
if ( $display === "list-item" ) {
// Insert a list-bullet frame
$xml = $dompdf->get_dom();
$bullet_node = $xml->createElement("bullet"); // arbitrary choice
$b_f = new Frame($bullet_node);
$node = $frame->get_node();
$parent_node = $node->parentNode;
if ( $parent_node ) {
if ( !$parent_node->hasAttribute("dompdf-children-count") ) {
$xpath = new DOMXPath($xml);
$count = $xpath->query("li", $parent_node)->length;
$parent_node->setAttribute("dompdf-children-count", $count);
}
if ( is_numeric($node->getAttribute("value")) ) {
$index = intval($node->getAttribute("value"));
}
else {
if ( !$parent_node->hasAttribute("dompdf-counter") ) {
$index = ($parent_node->hasAttribute("start") ? $parent_node->getAttribute("start") : 1);
}
else {
$index = $parent_node->getAttribute("dompdf-counter")+1;
}
}
$parent_node->setAttribute("dompdf-counter", $index);
$bullet_node->setAttribute("dompdf-counter", $index);
}
$new_style = $dompdf->get_css()->create_style();
$new_style->display = "-dompdf-list-bullet";
$new_style->inherit($style);
$b_f->set_style($new_style);
$deco->prepend_child( Frame_Factory::decorate_frame($b_f, $dompdf, $root) );
}
return $deco;
}
}

View File

@@ -0,0 +1,458 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Base reflower class
*
* Reflower objects are responsible for determining the width and height of
* individual frames. They also create line and page breaks as necessary.
*
* @access private
* @package dompdf
*/
abstract class Frame_Reflower {
/**
* Frame for this reflower
*
* @var Frame
*/
protected $_frame;
/**
* Cached min/max size
*
* @var array
*/
protected $_min_max_cache;
function __construct(Frame $frame) {
$this->_frame = $frame;
$this->_min_max_cache = null;
}
function dispose() {
clear_object($this);
}
/**
* @return DOMPDF
*/
function get_dompdf() {
return $this->_frame->get_dompdf();
}
/**
* Collapse frames margins
* http://www.w3.org/TR/CSS2/box.html#collapsing-margins
*/
protected function _collapse_margins() {
$frame = $this->_frame;
$cb = $frame->get_containing_block();
$style = $frame->get_style();
if ( !$frame->is_in_flow() ) {
return;
}
$t = $style->length_in_pt($style->margin_top, $cb["h"]);
$b = $style->length_in_pt($style->margin_bottom, $cb["h"]);
// Handle 'auto' values
if ( $t === "auto" ) {
$style->margin_top = "0pt";
$t = 0;
}
if ( $b === "auto" ) {
$style->margin_bottom = "0pt";
$b = 0;
}
// Collapse vertical margins:
$n = $frame->get_next_sibling();
if ( $n && !$n->is_block() ) {
while ( $n = $n->get_next_sibling() ) {
if ( $n->is_block() ) {
break;
}
if ( !$n->get_first_child() ) {
$n = null;
break;
}
}
}
if ( $n ) {
$n_style = $n->get_style();
$b = max($b, $n_style->length_in_pt($n_style->margin_top, $cb["h"]));
$n_style->margin_top = "0pt";
$style->margin_bottom = $b."pt";
}
// Collapse our first child's margin
/*$f = $this->_frame->get_first_child();
if ( $f && !$f->is_block() ) {
while ( $f = $f->get_next_sibling() ) {
if ( $f->is_block() ) {
break;
}
if ( !$f->get_first_child() ) {
$f = null;
break;
}
}
}
// Margin are collapsed only between block elements
if ( $f ) {
$f_style = $f->get_style();
$t = max($t, $f_style->length_in_pt($f_style->margin_top, $cb["h"]));
$style->margin_top = $t."pt";
$f_style->margin_bottom = "0pt";
}*/
}
//........................................................................
abstract function reflow(Block_Frame_Decorator $block = null);
//........................................................................
// Required for table layout: Returns an array(0 => min, 1 => max, "min"
// => min, "max" => max) of the minimum and maximum widths of this frame.
// This provides a basic implementation. Child classes should override
// this if necessary.
function get_min_max_width() {
if ( !is_null($this->_min_max_cache) ) {
return $this->_min_max_cache;
}
$style = $this->_frame->get_style();
// Account for margins & padding
$dims = array($style->padding_left,
$style->padding_right,
$style->border_left_width,
$style->border_right_width,
$style->margin_left,
$style->margin_right);
$cb_w = $this->_frame->get_containing_block("w");
$delta = $style->length_in_pt($dims, $cb_w);
// Handle degenerate case
if ( !$this->_frame->get_first_child() ) {
return $this->_min_max_cache = array(
$delta, $delta,
"min" => $delta,
"max" => $delta,
);
}
$low = array();
$high = array();
for ( $iter = $this->_frame->get_children()->getIterator();
$iter->valid();
$iter->next() ) {
$inline_min = 0;
$inline_max = 0;
// Add all adjacent inline widths together to calculate max width
while ( $iter->valid() && in_array( $iter->current()->get_style()->display, Style::$INLINE_TYPES ) ) {
$child = $iter->current();
$minmax = $child->get_min_max_width();
if ( in_array( $iter->current()->get_style()->white_space, array("pre", "nowrap") ) ) {
$inline_min += $minmax["min"];
}
else {
$low[] = $minmax["min"];
}
$inline_max += $minmax["max"];
$iter->next();
}
if ( $inline_max > 0 ) $high[] = $inline_max;
if ( $inline_min > 0 ) $low[] = $inline_min;
if ( $iter->valid() ) {
list($low[], $high[]) = $iter->current()->get_min_max_width();
continue;
}
}
$min = count($low) ? max($low) : 0;
$max = count($high) ? max($high) : 0;
// Use specified width if it is greater than the minimum defined by the
// content. If the width is a percentage ignore it for now.
$width = $style->width;
if ( $width !== "auto" && !is_percent($width) ) {
$width = $style->length_in_pt($width, $cb_w);
if ( $min < $width ) $min = $width;
if ( $max < $width ) $max = $width;
}
$min += $delta;
$max += $delta;
return $this->_min_max_cache = array($min, $max, "min"=>$min, "max"=>$max);
}
/**
* Parses a CSS string containing quotes and escaped hex characters
*
* @param $string string The CSS string to parse
* @param $single_trim
* @return string
*/
protected function _parse_string($string, $single_trim = false) {
if ( $single_trim ) {
$string = preg_replace('/^[\"\']/', "", $string);
$string = preg_replace('/[\"\']$/', "", $string);
}
else {
$string = trim($string, "'\"");
}
$string = str_replace(array("\\\n",'\\"',"\\'"),
array("",'"',"'"), $string);
// Convert escaped hex characters into ascii characters (e.g. \A => newline)
$string = preg_replace_callback("/\\\\([0-9a-fA-F]{0,6})/",
create_function('$matches',
'return unichr(hexdec($matches[1]));'),
$string);
return $string;
}
/**
* Parses a CSS "quotes" property
*
* @return array|null An array of pairs of quotes
*/
protected function _parse_quotes() {
// Matches quote types
$re = '/(\'[^\']*\')|(\"[^\"]*\")/';
$quotes = $this->_frame->get_style()->quotes;
// split on spaces, except within quotes
if ( !preg_match_all($re, "$quotes", $matches, PREG_SET_ORDER) ) {
return null;
}
$quotes_array = array();
foreach($matches as &$_quote){
$quotes_array[] = $this->_parse_string($_quote[0], true);
}
if ( empty($quotes_array) ) {
$quotes_array = array('"', '"');
}
return array_chunk($quotes_array, 2);
}
/**
* Parses the CSS "content" property
*
* @return string|null The resulting string
*/
protected function _parse_content() {
// Matches generated content
$re = "/\n".
"\s(counters?\\([^)]*\\))|\n".
"\A(counters?\\([^)]*\\))|\n".
"\s([\"']) ( (?:[^\"']|\\\\[\"'])+ )(?<!\\\\)\\3|\n".
"\A([\"']) ( (?:[^\"']|\\\\[\"'])+ )(?<!\\\\)\\5|\n" .
"\s([^\s\"']+)|\n" .
"\A([^\s\"']+)\n".
"/xi";
$content = $this->_frame->get_style()->content;
$quotes = $this->_parse_quotes();
// split on spaces, except within quotes
if ( !preg_match_all($re, $content, $matches, PREG_SET_ORDER) ) {
return null;
}
$text = "";
foreach ($matches as $match) {
if ( isset($match[2]) && $match[2] !== "" ) {
$match[1] = $match[2];
}
if ( isset($match[6]) && $match[6] !== "" ) {
$match[4] = $match[6];
}
if ( isset($match[8]) && $match[8] !== "" ) {
$match[7] = $match[8];
}
if ( isset($match[1]) && $match[1] !== "" ) {
// counters?(...)
$match[1] = mb_strtolower(trim($match[1]));
// Handle counter() references:
// http://www.w3.org/TR/CSS21/generate.html#content
$i = mb_strpos($match[1], ")");
if ( $i === false ) {
continue;
}
preg_match( '/(counters?)(^\()*?\(\s*([^\s,]+)\s*(,\s*["\']?([^"\'\)]+)["\']?\s*(,\s*([^\s)]+)\s*)?)?\)/i' , $match[1] , $args );
$counter_id = $args[3];
if ( strtolower( $args[1] ) == 'counter' ) {
// counter(name [,style])
if ( isset( $args[5] ) ) {
$type = trim( $args[5] );
}
else {
$type = null;
}
$p = $this->_frame->lookup_counter_frame( $counter_id );
$text .= $p->counter_value($counter_id, $type);
}
else if ( strtolower( $args[1] ) == 'counters' ) {
// counters(name, string [,style])
if ( isset($args[5]) ) {
$string = $this->_parse_string( $args[5] );
}
else {
$string = "";
}
if ( isset( $args[7] ) ) {
$type = trim( $args[7] );
}
else {
$type = null;
}
$p = $this->_frame->lookup_counter_frame($counter_id);
$tmp = array();
while ($p) {
// We only want to use the counter values when they actually increment the counter
if ( array_key_exists( $counter_id , $p->_counters ) ) {
array_unshift( $tmp , $p->counter_value($counter_id, $type) );
}
$p = $p->lookup_counter_frame($counter_id);
}
$text .= implode( $string , $tmp );
}
else {
// countertops?
continue;
}
}
else if ( isset($match[4]) && $match[4] !== "" ) {
// String match
$text .= $this->_parse_string($match[4]);
}
else if ( isset($match[7]) && $match[7] !== "" ) {
// Directive match
if ( $match[7] === "open-quote" ) {
// FIXME: do something here
$text .= $quotes[0][0];
}
else if ( $match[7] === "close-quote" ) {
// FIXME: do something else here
$text .= $quotes[0][1];
}
else if ( $match[7] === "no-open-quote" ) {
// FIXME:
}
else if ( $match[7] === "no-close-quote" ) {
// FIXME:
}
else if ( mb_strpos($match[7],"attr(") === 0 ) {
$i = mb_strpos($match[7],")");
if ( $i === false ) {
continue;
}
$attr = mb_substr($match[7], 5, $i - 5);
if ( $attr == "" ) {
continue;
}
$text .= $this->_frame->get_parent()->get_node()->getAttribute($attr);
}
else {
continue;
}
}
}
return $text;
}
/**
* Sets the generated content of a generated frame
*/
protected function _set_content(){
$frame = $this->_frame;
$style = $frame->get_style();
// if the element was pushed to a new page use the saved counter value, otherwise use the CSS reset value
if ( $style->counter_reset && ($reset = $style->counter_reset) !== "none" ) {
$vars = preg_split('/\s+/', trim($reset), 2);
$frame->reset_counter( $vars[0] , ( isset($frame->_counters['__'.$vars[0]]) ? $frame->_counters['__'.$vars[0]] : ( isset($vars[1]) ? $vars[1] : 0 ) ) );
}
if ( $style->counter_increment && ($increment = $style->counter_increment) !== "none" ) {
$frame->increment_counters($increment);
}
if ( $style->content && !$frame->get_first_child() && $frame->get_node()->nodeName === "dompdf_generated" ) {
$content = $this->_parse_content();
// add generated content to the font subset
// FIXME: This is currently too late because the font subset has already been generated.
// See notes in issue #750.
if ( $frame->get_dompdf()->get_option("enable_font_subsetting") && $frame->get_dompdf()->get_canvas() instanceof CPDF_Adapter ) {
$frame->get_dompdf()->get_canvas()->register_string_subset($style->font_family, $content);
}
$node = $frame->get_node()->ownerDocument->createTextNode($content);
$new_style = $style->get_stylesheet()->create_style();
$new_style->inherit($style);
$new_frame = new Frame($node);
$new_frame->set_style($new_style);
Frame_Factory::decorate_frame($new_frame, $frame->get_dompdf(), $frame->get_root());
$frame->append_child($new_frame);
}
}
}

View File

@@ -0,0 +1,241 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Represents an entire document as a tree of frames
*
* The Frame_Tree consists of {@link Frame} objects each tied to specific
* DOMNode objects in a specific DomDocument. The Frame_Tree has the same
* structure as the DomDocument, but adds additional capabalities for
* styling and layout.
*
* @package dompdf
* @access protected
*/
class Frame_Tree {
/**
* Tags to ignore while parsing the tree
*
* @var array
*/
static protected $_HIDDEN_TAGS = array("area", "base", "basefont", "head", "style",
"meta", "title", "colgroup",
"noembed", "noscript", "param", "#comment");
/**
* The main DomDocument
*
* @see http://ca2.php.net/manual/en/ref.dom.php
* @var DomDocument
*/
protected $_dom;
/**
* The root node of the FrameTree.
*
* @var Frame
*/
protected $_root;
/**
* Subtrees of absolutely positioned elements
*
* @var array of Frames
*/
protected $_absolute_frames;
/**
* A mapping of {@link Frame} objects to DOMNode objects
*
* @var array
*/
protected $_registry;
/**
* Class constructor
*
* @param DomDocument $dom the main DomDocument object representing the current html document
*/
function __construct(DomDocument $dom) {
$this->_dom = $dom;
$this->_root = null;
$this->_registry = array();
}
function __destruct() {
clear_object($this);
}
/**
* Returns the DomDocument object representing the curent html document
*
* @return DOMDocument
*/
function get_dom() {
return $this->_dom;
}
/**
* Returns the root frame of the tree
*
* @return Page_Frame_Decorator
*/
function get_root() {
return $this->_root;
}
/**
* Returns a specific frame given its id
*
* @param string $id
* @return Frame
*/
function get_frame($id) {
return isset($this->_registry[$id]) ? $this->_registry[$id] : null;
}
/**
* Returns a post-order iterator for all frames in the tree
*
* @return FrameTreeList|Frame[]
*/
function get_frames() {
return new FrameTreeList($this->_root);
}
/**
* Builds the tree
*/
function build_tree() {
$html = $this->_dom->getElementsByTagName("html")->item(0);
if ( is_null($html) ) {
$html = $this->_dom->firstChild;
}
if ( is_null($html) ) {
throw new DOMPDF_Exception("Requested HTML document contains no data.");
}
$this->fix_tables();
$this->_root = $this->_build_tree_r($html);
}
/**
* Adds missing TBODYs around TR
*/
protected function fix_tables(){
$xp = new DOMXPath($this->_dom);
// Move table caption before the table
// FIXME find a better way to deal with it...
$captions = $xp->query("//table/caption");
foreach($captions as $caption) {
$table = $caption->parentNode;
$table->parentNode->insertBefore($caption, $table);
}
$rows = $xp->query("//table/tr");
foreach($rows as $row) {
$tbody = $this->_dom->createElement("tbody");
$tbody = $row->parentNode->insertBefore($tbody, $row);
$tbody->appendChild($row);
}
}
/**
* Recursively adds {@link Frame} objects to the tree
*
* Recursively build a tree of Frame objects based on a dom tree.
* No layout information is calculated at this time, although the
* tree may be adjusted (i.e. nodes and frames for generated content
* and images may be created).
*
* @param DOMNode $node the current DOMNode being considered
* @return Frame
*/
protected function _build_tree_r(DOMNode $node) {
$frame = new Frame($node);
$id = $frame->get_id();
$this->_registry[ $id ] = $frame;
if ( !$node->hasChildNodes() ) {
return $frame;
}
// Fixes 'cannot access undefined property for object with
// overloaded access', fix by Stefan radulian
// <stefan.radulian@symbion.at>
//foreach ($node->childNodes as $child) {
// Store the children in an array so that the tree can be modified
$children = array();
for ($i = 0; $i < $node->childNodes->length; $i++) {
$children[] = $node->childNodes->item($i);
}
foreach ($children as $child) {
$node_name = mb_strtolower($child->nodeName);
// Skip non-displaying nodes
if ( in_array($node_name, self::$_HIDDEN_TAGS) ) {
if ( $node_name !== "head" && $node_name !== "style" ) {
$child->parentNode->removeChild($child);
}
continue;
}
// Skip empty text nodes
if ( $node_name === "#text" && $child->nodeValue == "" ) {
$child->parentNode->removeChild($child);
continue;
}
// Skip empty image nodes
if ( $node_name === "img" && $child->getAttribute("src") == "" ) {
$child->parentNode->removeChild($child);
continue;
}
$frame->append_child($this->_build_tree_r($child), false);
}
return $frame;
}
public function insert_node(DOMNode $node, DOMNode $new_node, $pos) {
if ( $pos === "after" || !$node->firstChild ) {
$node->appendChild($new_node);
}
else {
$node->insertBefore($new_node, $node->firstChild);
}
$this->_build_tree_r($new_node);
$frame_id = $new_node->getAttribute("frame_id");
$frame = $this->get_frame($frame_id);
$parent_id = $node->getAttribute("frame_id");
$parent = $this->get_frame($parent_id);
if ( $parent ) {
if ( $pos === "before" ) {
$parent->prepend_child($frame, false);
}
else {
$parent->append_child($frame, false);
}
}
return $frame_id;
}
}

View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,840 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author Fabien Ménager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Image rendering interface
*
* Renders to an image format supported by GD (jpeg, gif, png, xpm).
* Not super-useful day-to-day but handy nonetheless
*
* @package dompdf
*/
class GD_Adapter implements Canvas {
/**
* @var DOMPDF
*/
private $_dompdf;
/**
* Resource handle for the image
*
* @var resource
*/
private $_img;
/**
* Image width in pixels
*
* @var int
*/
private $_width;
/**
* Image height in pixels
*
* @var int
*/
private $_height;
/**
* Current page number
*
* @var int
*/
private $_page_number;
/**
* Total number of pages
*
* @var int
*/
private $_page_count;
/**
* Image antialias factor
*
* @var float
*/
private $_aa_factor;
/**
* Allocated colors
*
* @var array
*/
private $_colors;
/**
* Background color
*
* @var int
*/
private $_bg_color;
/**
* Class constructor
*
* @param mixed $size The size of image to create: array(x1,y1,x2,y2) or "letter", "legal", etc.
* @param string $orientation The orientation of the document (either 'landscape' or 'portrait')
* @param DOMPDF $dompdf
* @param float $aa_factor Anti-aliasing factor, 1 for no AA
* @param array $bg_color Image background color: array(r,g,b,a), 0 <= r,g,b,a <= 1
*/
function __construct($size, $orientation = "portrait", DOMPDF $dompdf, $aa_factor = 1.0, $bg_color = array(1,1,1,0) ) {
if ( !is_array($size) ) {
$size = strtolower($size);
if ( isset(CPDF_Adapter::$PAPER_SIZES[$size]) ) {
$size = CPDF_Adapter::$PAPER_SIZES[$size];
}
else {
$size = CPDF_Adapter::$PAPER_SIZES["letter"];
}
}
if ( strtolower($orientation) === "landscape" ) {
list($size[2],$size[3]) = array($size[3],$size[2]);
}
$this->_dompdf = $dompdf;
if ( $aa_factor < 1 ) {
$aa_factor = 1;
}
$this->_aa_factor = $aa_factor;
$size[2] *= $aa_factor;
$size[3] *= $aa_factor;
$this->_width = $size[2] - $size[0];
$this->_height = $size[3] - $size[1];
$this->_img = imagecreatetruecolor($this->_width, $this->_height);
if ( is_null($bg_color) || !is_array($bg_color) ) {
// Pure white bg
$bg_color = array(1,1,1,0);
}
$this->_bg_color = $this->_allocate_color($bg_color);
imagealphablending($this->_img, true);
imagesavealpha($this->_img, true);
imagefill($this->_img, 0, 0, $this->_bg_color);
}
function get_dompdf(){
return $this->_dompdf;
}
/**
* Return the GF image resource
*
* @return resource
*/
function get_image() { return $this->_img; }
/**
* Return the image's width in pixels
*
* @return float
*/
function get_width() { return $this->_width / $this->_aa_factor; }
/**
* Return the image's height in pixels
*
* @return float
*/
function get_height() { return $this->_height / $this->_aa_factor; }
/**
* Returns the current page number
* @return int
*/
function get_page_number() { return $this->_page_number; }
/**
* Returns the total number of pages in the document
* @return int
*/
function get_page_count() { return $this->_page_count; }
/**
* Sets the current page number
*
* @param int $num
*/
function set_page_number($num) { $this->_page_number = $num; }
/**
* Sets the page count
*
* @param int $count
*/
function set_page_count($count) { $this->_page_count = $count; }
/**
* Sets the opacity
*
* @param $opacity
* @param $mode
*/
function set_opacity($opacity, $mode = "Normal") {
// FIXME
}
/**
* Allocate a new color. Allocate with GD as needed and store
* previously allocated colors in $this->_colors.
*
* @param array $color The new current color
* @return int The allocated color
*/
private function _allocate_color($color) {
if ( isset($color["c"]) ) {
$color = cmyk_to_rgb($color);
}
// Full opacity if no alpha set
if ( !isset($color[3]) )
$color[3] = 0;
list($r,$g,$b,$a) = $color;
$r *= 255;
$g *= 255;
$b *= 255;
$a *= 127;
// Clip values
$r = $r > 255 ? 255 : $r;
$g = $g > 255 ? 255 : $g;
$b = $b > 255 ? 255 : $b;
$a = $a > 127 ? 127 : $a;
$r = $r < 0 ? 0 : $r;
$g = $g < 0 ? 0 : $g;
$b = $b < 0 ? 0 : $b;
$a = $a < 0 ? 0 : $a;
$key = sprintf("#%02X%02X%02X%02X", $r, $g, $b, $a);
if ( isset($this->_colors[$key]) )
return $this->_colors[$key];
if ( $a != 0 )
$this->_colors[$key] = imagecolorallocatealpha($this->_img, $r, $g, $b, $a);
else
$this->_colors[$key] = imagecolorallocate($this->_img, $r, $g, $b);
return $this->_colors[$key];
}
/**
* Draws a line from x1,y1 to x2,y2
*
* See {@link Style::munge_color()} for the format of the color array.
* See {@link Cpdf::setLineStyle()} for a description of the format of the
* $style parameter (aka dash).
*
* @param float $x1
* @param float $y1
* @param float $x2
* @param float $y2
* @param array $color
* @param float $width
* @param array $style
*/
function line($x1, $y1, $x2, $y2, $color, $width, $style = null) {
// Scale by the AA factor
$x1 *= $this->_aa_factor;
$y1 *= $this->_aa_factor;
$x2 *= $this->_aa_factor;
$y2 *= $this->_aa_factor;
$width *= $this->_aa_factor;
$c = $this->_allocate_color($color);
// Convert the style array if required
if ( !is_null($style) ) {
$gd_style = array();
if ( count($style) == 1 ) {
for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++) {
$gd_style[] = $c;
}
for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++) {
$gd_style[] = $this->_bg_color;
}
} else {
$i = 0;
foreach ($style as $length) {
if ( $i % 2 == 0 ) {
// 'On' pattern
for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++)
$gd_style[] = $c;
} else {
// Off pattern
for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++)
$gd_style[] = $this->_bg_color;
}
$i++;
}
}
imagesetstyle($this->_img, $gd_style);
$c = IMG_COLOR_STYLED;
}
imagesetthickness($this->_img, $width);
imageline($this->_img, $x1, $y1, $x2, $y2, $c);
}
function arc($x1, $y1, $r1, $r2, $astart, $aend, $color, $width, $style = array()) {
// @todo
}
/**
* Draws a rectangle at x1,y1 with width w and height h
*
* See {@link Style::munge_color()} for the format of the color array.
* See {@link Cpdf::setLineStyle()} for a description of the $style
* parameter (aka dash)
*
* @param float $x1
* @param float $y1
* @param float $w
* @param float $h
* @param array $color
* @param float $width
* @param array $style
*/
function rectangle($x1, $y1, $w, $h, $color, $width, $style = null) {
// Scale by the AA factor
$x1 *= $this->_aa_factor;
$y1 *= $this->_aa_factor;
$w *= $this->_aa_factor;
$h *= $this->_aa_factor;
$c = $this->_allocate_color($color);
// Convert the style array if required
if ( !is_null($style) ) {
$gd_style = array();
foreach ($style as $length) {
for ($i = 0; $i < $length; $i++) {
$gd_style[] = $c;
}
}
imagesetstyle($this->_img, $gd_style);
$c = IMG_COLOR_STYLED;
}
imagesetthickness($this->_img, $width);
imagerectangle($this->_img, $x1, $y1, $x1 + $w, $y1 + $h, $c);
}
/**
* Draws a filled rectangle at x1,y1 with width w and height h
*
* See {@link Style::munge_color()} for the format of the color array.
*
* @param float $x1
* @param float $y1
* @param float $w
* @param float $h
* @param array $color
*/
function filled_rectangle($x1, $y1, $w, $h, $color) {
// Scale by the AA factor
$x1 *= $this->_aa_factor;
$y1 *= $this->_aa_factor;
$w *= $this->_aa_factor;
$h *= $this->_aa_factor;
$c = $this->_allocate_color($color);
imagefilledrectangle($this->_img, $x1, $y1, $x1 + $w, $y1 + $h, $c);
}
/**
* Starts a clipping rectangle at x1,y1 with width w and height h
*
* @param float $x1
* @param float $y1
* @param float $w
* @param float $h
*/
function clipping_rectangle($x1, $y1, $w, $h) {
// @todo
}
function clipping_roundrectangle($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL) {
// @todo
}
/**
* Ends the last clipping shape
*/
function clipping_end() {
// @todo
}
function save() {
// @todo
}
function restore() {
// @todo
}
function rotate($angle, $x, $y) {
// @todo
}
function skew($angle_x, $angle_y, $x, $y) {
// @todo
}
function scale($s_x, $s_y, $x, $y) {
// @todo
}
function translate($t_x, $t_y) {
// @todo
}
function transform($a, $b, $c, $d, $e, $f) {
// @todo
}
/**
* Draws a polygon
*
* The polygon is formed by joining all the points stored in the $points
* array. $points has the following structure:
* <code>
* array(0 => x1,
* 1 => y1,
* 2 => x2,
* 3 => y2,
* ...
* );
* </code>
*
* See {@link Style::munge_color()} for the format of the color array.
* See {@link Cpdf::setLineStyle()} for a description of the $style
* parameter (aka dash)
*
* @param array $points
* @param array $color
* @param float $width
* @param array $style
* @param bool $fill Fills the polygon if true
*/
function polygon($points, $color, $width = null, $style = null, $fill = false) {
// Scale each point by the AA factor
foreach (array_keys($points) as $i)
$points[$i] *= $this->_aa_factor;
$c = $this->_allocate_color($color);
// Convert the style array if required
if ( !is_null($style) && !$fill ) {
$gd_style = array();
foreach ($style as $length) {
for ($i = 0; $i < $length; $i++) {
$gd_style[] = $c;
}
}
imagesetstyle($this->_img, $gd_style);
$c = IMG_COLOR_STYLED;
}
imagesetthickness($this->_img, $width);
if ( $fill )
imagefilledpolygon($this->_img, $points, count($points) / 2, $c);
else
imagepolygon($this->_img, $points, count($points) / 2, $c);
}
/**
* Draws a circle at $x,$y with radius $r
*
* See {@link Style::munge_color()} for the format of the color array.
* See {@link Cpdf::setLineStyle()} for a description of the $style
* parameter (aka dash)
*
* @param float $x
* @param float $y
* @param float $r
* @param array $color
* @param float $width
* @param array $style
* @param bool $fill Fills the circle if true
*/
function circle($x, $y, $r, $color, $width = null, $style = null, $fill = false) {
// Scale by the AA factor
$x *= $this->_aa_factor;
$y *= $this->_aa_factor;
$r *= $this->_aa_factor;
$c = $this->_allocate_color($color);
// Convert the style array if required
if ( !is_null($style) && !$fill ) {
$gd_style = array();
foreach ($style as $length) {
for ($i = 0; $i < $length; $i++) {
$gd_style[] = $c;
}
}
imagesetstyle($this->_img, $gd_style);
$c = IMG_COLOR_STYLED;
}
imagesetthickness($this->_img, $width);
if ( $fill )
imagefilledellipse($this->_img, $x, $y, $r, $r, $c);
else
imageellipse($this->_img, $x, $y, $r, $r, $c);
}
/**
* Add an image to the pdf.
* The image is placed at the specified x and y coordinates with the
* given width and height.
*
* @param string $img_url the path to the image
* @param float $x x position
* @param float $y y position
* @param int $w width (in pixels)
* @param int $h height (in pixels)
* @param string $resolution
*
* @return void
* @internal param string $img_type the type (e.g. extension) of the image
*/
function image($img_url, $x, $y, $w, $h, $resolution = "normal") {
$img_type = Image_Cache::detect_type($img_url);
$img_ext = Image_Cache::type_to_ext($img_type);
if ( !$img_ext ) {
return;
}
$func = "imagecreatefrom$img_ext";
$src = @$func($img_url);
if ( !$src ) {
return; // Probably should add to $_dompdf_errors or whatever here
}
// Scale by the AA factor
$x *= $this->_aa_factor;
$y *= $this->_aa_factor;
$w *= $this->_aa_factor;
$h *= $this->_aa_factor;
$img_w = imagesx($src);
$img_h = imagesy($src);
imagecopyresampled($this->_img, $src, $x, $y, 0, 0, $w, $h, $img_w, $img_h);
}
/**
* Writes text at the specified x and y coordinates
* See {@link Style::munge_color()} for the format of the color array.
*
* @param float $x
* @param float $y
* @param string $text the text to write
* @param string $font the font file to use
* @param float $size the font size, in points
* @param array $color
* @param float $word_spacing word spacing adjustment
* @param float $char_spacing
* @param float $angle Text angle
*
* @return void
*/
function text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_spacing = 0.0, $char_spacing = 0.0, $angle = 0.0) {
// Scale by the AA factor
$x *= $this->_aa_factor;
$y *= $this->_aa_factor;
$size *= $this->_aa_factor;
$h = $this->get_font_height($font, $size);
$c = $this->_allocate_color($color);
$text = mb_encode_numericentity($text, array(0x0080, 0xff, 0, 0xff), 'UTF-8');
$font = $this->get_ttf_file($font);
// FIXME: word spacing
@imagettftext($this->_img, $size, $angle, $x, $y + $h, $c, $font, $text);
}
function javascript($code) {
// Not implemented
}
/**
* Add a named destination (similar to <a name="foo">...</a> in html)
*
* @param string $anchorname The name of the named destination
*/
function add_named_dest($anchorname) {
// Not implemented
}
/**
* Add a link to the pdf
*
* @param string $url The url to link to
* @param float $x The x position of the link
* @param float $y The y position of the link
* @param float $width The width of the link
* @param float $height The height of the link
*/
function add_link($url, $x, $y, $width, $height) {
// Not implemented
}
/**
* Add meta information to the PDF
*
* @param string $label label of the value (Creator, Producer, etc.)
* @param string $value the text to set
*/
function add_info($label, $value) {
// N/A
}
function set_default_view($view, $options = array()) {
// N/A
}
/**
* Calculates text size, in points
*
* @param string $text the text to be sized
* @param string $font the desired font
* @param float $size the desired font size
* @param float $word_spacing word spacing, if any
* @param float $char_spacing char spacing, if any
*
* @return float
*/
function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0) {
$font = $this->get_ttf_file($font);
$text = mb_encode_numericentity($text, array(0x0080, 0xffff, 0, 0xffff), 'UTF-8');
// FIXME: word spacing
list($x1,,$x2) = @imagettfbbox($size, 0, $font, $text);
return $x2 - $x1;
}
function get_ttf_file($font) {
if ( strpos($font, '.ttf') === false )
$font .= ".ttf";
/*$filename = substr(strtolower(basename($font)), 0, -4);
if ( in_array($filename, DOMPDF::$native_fonts) ) {
return "arial.ttf";
}*/
return $font;
}
/**
* Calculates font height, in points
*
* @param string $font
* @param float $size
* @return float
*/
function get_font_height($font, $size) {
$font = $this->get_ttf_file($font);
$ratio = $this->_dompdf->get_option("font_height_ratio");
// FIXME: word spacing
list(,$y2,,,,$y1) = imagettfbbox($size, 0, $font, "MXjpqytfhl"); // Test string with ascenders, descenders and caps
return ($y2 - $y1) * $ratio;
}
function get_font_baseline($font, $size) {
$ratio = $this->_dompdf->get_option("font_height_ratio");
return $this->get_font_height($font, $size) / $ratio;
}
/**
* Starts a new page
*
* Subsequent drawing operations will appear on the new page.
*/
function new_page() {
$this->_page_number++;
$this->_page_count++;
}
function open_object(){
// N/A
}
function close_object(){
// N/A
}
function add_object(){
// N/A
}
function page_text(){
// N/A
}
/**
* Streams the image directly to the browser
*
* @param string $filename the name of the image file (ignored)
* @param array $options associative array, 'type' => jpeg|jpg|png, 'quality' => 0 - 100 (jpeg only)
*/
function stream($filename, $options = null) {
// Perform any antialiasing
if ( $this->_aa_factor != 1 ) {
$dst_w = $this->_width / $this->_aa_factor;
$dst_h = $this->_height / $this->_aa_factor;
$dst = imagecreatetruecolor($dst_w, $dst_h);
imagecopyresampled($dst, $this->_img, 0, 0, 0, 0,
$dst_w, $dst_h,
$this->_width, $this->_height);
} else {
$dst = $this->_img;
}
if ( !isset($options["type"]) )
$options["type"] = "png";
$type = strtolower($options["type"]);
header("Cache-Control: private");
switch ($type) {
case "jpg":
case "jpeg":
if ( !isset($options["quality"]) )
$options["quality"] = 75;
header("Content-type: image/jpeg");
imagejpeg($dst, '', $options["quality"]);
break;
case "png":
default:
header("Content-type: image/png");
imagepng($dst);
break;
}
if ( $this->_aa_factor != 1 )
imagedestroy($dst);
}
/**
* Returns the PNG as a string
*
* @param array $options associative array, 'type' => jpeg|jpg|png, 'quality' => 0 - 100 (jpeg only)
* @return string
*/
function output($options = null) {
if ( $this->_aa_factor != 1 ) {
$dst_w = $this->_width / $this->_aa_factor;
$dst_h = $this->_height / $this->_aa_factor;
$dst = imagecreatetruecolor($dst_w, $dst_h);
imagecopyresampled($dst, $this->_img, 0, 0, 0, 0,
$dst_w, $dst_h,
$this->_width, $this->_height);
} else {
$dst = $this->_img;
}
if ( !isset($options["type"]) )
$options["type"] = "png";
$type = $options["type"];
ob_start();
switch ($type) {
case "jpg":
case "jpeg":
if ( !isset($options["quality"]) )
$options["quality"] = 75;
imagejpeg($dst, '', $options["quality"]);
break;
case "png":
default:
imagepng($dst);
break;
}
$image = ob_get_clean();
if ( $this->_aa_factor != 1 )
imagedestroy($dst);
return $image;
}
}

View File

@@ -0,0 +1,183 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author Helmut Tischer <htischer@weihenstephan.org>
* @author Fabien Ménager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Static class that resolves image urls and downloads and caches
* remote images if required.
*
* @access private
* @package dompdf
*/
class Image_Cache {
/**
* Array of downloaded images. Cached so that identical images are
* not needlessly downloaded.
*
* @var array
*/
static protected $_cache = array();
/**
* The url to the "broken image" used when images can't be loade
*
* @var string
*/
public static $broken_image;
/**
* Resolve and fetch an image for use.
*
* @param string $url The url of the image
* @param string $protocol Default protocol if none specified in $url
* @param string $host Default host if none specified in $url
* @param string $base_path Default path if none specified in $url
* @param DOMPDF $dompdf The DOMPDF instance
*
* @throws DOMPDF_Image_Exception
* @return array An array with two elements: The local path to the image and the image extension
*/
static function resolve_url($url, $protocol, $host, $base_path, DOMPDF $dompdf) {
$parsed_url = explode_url($url);
$message = null;
$remote = ($protocol && $protocol !== "file://") || ($parsed_url['protocol'] != "");
$data_uri = strpos($parsed_url['protocol'], "data:") === 0;
$full_url = null;
$enable_remote = $dompdf->get_option("enable_remote");
try {
// Remote not allowed and is not DataURI
if ( !$enable_remote && $remote && !$data_uri ) {
throw new DOMPDF_Image_Exception("DOMPDF_ENABLE_REMOTE is set to FALSE");
}
// Remote allowed or DataURI
else if ( $enable_remote && $remote || $data_uri ) {
// Download remote files to a temporary directory
$full_url = build_url($protocol, $host, $base_path, $url);
// From cache
if ( isset(self::$_cache[$full_url]) ) {
$resolved_url = self::$_cache[$full_url];
}
// From remote
else {
$tmp_dir = $dompdf->get_option("temp_dir");
$resolved_url = tempnam($tmp_dir, "ca_dompdf_img_");
$image = "";
if ($data_uri) {
if ($parsed_data_uri = parse_data_uri($url)) {
$image = $parsed_data_uri['data'];
}
}
else {
set_error_handler("record_warnings");
$image = file_get_contents($full_url);
restore_error_handler();
}
// Image not found or invalid
if ( strlen($image) == 0 ) {
$msg = ($data_uri ? "Data-URI could not be parsed" : "Image not found");
throw new DOMPDF_Image_Exception($msg);
}
// Image found, put in cache and process
else {
//e.g. fetch.php?media=url.jpg&cache=1
//- Image file name might be one of the dynamic parts of the url, don't strip off!
//- a remote url does not need to have a file extension at all
//- local cached file does not have a matching file extension
//Therefore get image type from the content
file_put_contents($resolved_url, $image);
}
}
}
// Not remote, local image
else {
$resolved_url = build_url($protocol, $host, $base_path, $url);
}
// Check if the local file is readable
if ( !is_readable($resolved_url) || !filesize($resolved_url) ) {
throw new DOMPDF_Image_Exception("Image not readable or empty");
}
// Check is the file is an image
else {
list($width, $height, $type) = dompdf_getimagesize($resolved_url);
// Known image type
if ( $width && $height && in_array($type, array(IMAGETYPE_GIF, IMAGETYPE_PNG, IMAGETYPE_JPEG, IMAGETYPE_BMP)) ) {
//Don't put replacement image into cache - otherwise it will be deleted on cache cleanup.
//Only execute on successful caching of remote image.
if ( $enable_remote && $remote || $data_uri ) {
self::$_cache[$full_url] = $resolved_url;
}
}
// Unknown image type
else {
throw new DOMPDF_Image_Exception("Image type unknown");
}
}
}
catch(DOMPDF_Image_Exception $e) {
$resolved_url = self::$broken_image;
$type = IMAGETYPE_PNG;
$message = $e->getMessage()." \n $url";
}
return array($resolved_url, $type, $message);
}
/**
* Unlink all cached images (i.e. temporary images either downloaded
* or converted)
*/
static function clear() {
if ( empty(self::$_cache) || DEBUGKEEPTEMP ) return;
foreach ( self::$_cache as $file ) {
if (DEBUGPNG) print "[clear unlink $file]";
unlink($file);
}
self::$_cache = array();
}
static function detect_type($file) {
list(, , $type) = dompdf_getimagesize($file);
return $type;
}
static function type_to_ext($type) {
$image_types = array(
IMAGETYPE_GIF => "gif",
IMAGETYPE_PNG => "png",
IMAGETYPE_JPEG => "jpeg",
IMAGETYPE_BMP => "bmp",
);
return (isset($image_types[$type]) ? $image_types[$type] : null);
}
static function is_broken($url) {
return $url === self::$broken_image;
}
}
Image_Cache::$broken_image = DOMPDF_LIB_DIR . "/res/broken_image.png";

View File

@@ -0,0 +1,80 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author Fabien Ménager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Decorates frames for image layout and rendering
*
* @access private
* @package dompdf
*/
class Image_Frame_Decorator extends Frame_Decorator {
/**
* The path to the image file (note that remote images are
* downloaded locally to DOMPDF_TEMP_DIR).
*
* @var string
*/
protected $_image_url;
/**
* The image's file error message
*
* @var string
*/
protected $_image_msg;
/**
* Class constructor
*
* @param Frame $frame the frame to decorate
* @param DOMPDF $dompdf the document's dompdf object (required to resolve relative & remote urls)
*/
function __construct(Frame $frame, DOMPDF $dompdf) {
parent::__construct($frame, $dompdf);
$url = $frame->get_node()->getAttribute("src");
$debug_png = $dompdf->get_option("debug_png");
if ($debug_png) print '[__construct '.$url.']';
list($this->_image_url, /*$type*/, $this->_image_msg) = Image_Cache::resolve_url(
$url,
$dompdf->get_protocol(),
$dompdf->get_host(),
$dompdf->get_base_path(),
$dompdf
);
if ( Image_Cache::is_broken($this->_image_url) &&
$alt = $frame->get_node()->getAttribute("alt") ) {
$style = $frame->get_style();
$style->width = (4/3)*Font_Metrics::get_text_width($alt, $style->font_family, $style->font_size, $style->word_spacing);
$style->height = Font_Metrics::get_font_height($style->font_family, $style->font_size);
}
}
/**
* Return the image's url
*
* @return string The url of this image
*/
function get_image_url() {
return $this->_image_url;
}
/**
* Return the image's error message
*
* @return string The image's error message
*/
function get_image_msg() {
return $this->_image_msg;
}
}

View File

@@ -0,0 +1,186 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author Fabien Ménager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Image reflower class
*
* @access private
* @package dompdf
*/
class Image_Frame_Reflower extends Frame_Reflower {
function __construct(Image_Frame_Decorator $frame) {
parent::__construct($frame);
}
function reflow(Block_Frame_Decorator $block = null) {
$this->_frame->position();
//FLOAT
//$frame = $this->_frame;
//$page = $frame->get_root();
//$enable_css_float = $this->get_dompdf()->get_option("enable_css_float");
//if ($enable_css_float && $frame->get_style()->float !== "none" ) {
// $page->add_floating_frame($this);
//}
// Set the frame's width
$this->get_min_max_width();
if ( $block ) {
$block->add_frame_to_line($this->_frame);
}
}
function get_min_max_width() {
if (DEBUGPNG) {
// Determine the image's size. Time consuming. Only when really needed?
list($img_width, $img_height) = dompdf_getimagesize($this->_frame->get_image_url());
print "get_min_max_width() ".
$this->_frame->get_style()->width.' '.
$this->_frame->get_style()->height.';'.
$this->_frame->get_parent()->get_style()->width." ".
$this->_frame->get_parent()->get_style()->height.";".
$this->_frame->get_parent()->get_parent()->get_style()->width.' '.
$this->_frame->get_parent()->get_parent()->get_style()->height.';'.
$img_width. ' '.
$img_height.'|' ;
}
$style = $this->_frame->get_style();
$width_forced = true;
$height_forced = true;
//own style auto or invalid value: use natural size in px
//own style value: ignore suffix text including unit, use given number as px
//own style %: walk up parent chain until found available space in pt; fill available space
//
//special ignored unit: e.g. 10ex: e treated as exponent; x ignored; 10e completely invalid ->like auto
$width = ($style->width > 0 ? $style->width : 0);
if ( is_percent($width) ) {
$t = 0.0;
for ($f = $this->_frame->get_parent(); $f; $f = $f->get_parent()) {
$f_style = $f->get_style();
$t = $f_style->length_in_pt($f_style->width);
if ($t != 0) {
break;
}
}
$width = ((float)rtrim($width,"%") * $t)/100; //maybe 0
} elseif ( !mb_strpos($width, 'pt') ) {
// Don't set image original size if "%" branch was 0 or size not given.
// Otherwise aspect changed on %/auto combination for width/height
// Resample according to px per inch
// See also List_Bullet_Image_Frame_Decorator::__construct
$width = $style->length_in_pt($width);
}
$height = ($style->height > 0 ? $style->height : 0);
if ( is_percent($height) ) {
$t = 0.0;
for ($f = $this->_frame->get_parent(); $f; $f = $f->get_parent()) {
$f_style = $f->get_style();
$t = $f_style->length_in_pt($f_style->height);
if ($t != 0) {
break;
}
}
$height = ((float)rtrim($height,"%") * $t)/100; //maybe 0
} elseif ( !mb_strpos($height, 'pt') ) {
// Don't set image original size if "%" branch was 0 or size not given.
// Otherwise aspect changed on %/auto combination for width/height
// Resample according to px per inch
// See also List_Bullet_Image_Frame_Decorator::__construct
$height = $style->length_in_pt($height);
}
if ($width == 0 || $height == 0) {
// Determine the image's size. Time consuming. Only when really needed!
list($img_width, $img_height) = dompdf_getimagesize($this->_frame->get_image_url());
// don't treat 0 as error. Can be downscaled or can be catched elsewhere if image not readable.
// Resample according to px per inch
// See also List_Bullet_Image_Frame_Decorator::__construct
if ($width == 0 && $height == 0) {
$dpi = $this->_frame->get_dompdf()->get_option("dpi");
$width = (float)($img_width * 72) / $dpi;
$height = (float)($img_height * 72) / $dpi;
$width_forced = false;
$height_forced = false;
} elseif ($height == 0 && $width != 0) {
$height_forced = false;
$height = ($width / $img_width) * $img_height; //keep aspect ratio
} elseif ($width == 0 && $height != 0) {
$width_forced = false;
$width = ($height / $img_height) * $img_width; //keep aspect ratio
}
}
// Handle min/max width/height
if ( $style->min_width !== "none" ||
$style->max_width !== "none" ||
$style->min_height !== "none" ||
$style->max_height !== "none" ) {
list(/*$x*/, /*$y*/, $w, $h) = $this->_frame->get_containing_block();
$min_width = $style->length_in_pt($style->min_width, $w);
$max_width = $style->length_in_pt($style->max_width, $w);
$min_height = $style->length_in_pt($style->min_height, $h);
$max_height = $style->length_in_pt($style->max_height, $h);
if ( $max_width !== "none" && $width > $max_width ) {
if ( !$height_forced ) {
$height *= $max_width / $width;
}
$width = $max_width;
}
if ( $min_width !== "none" && $width < $min_width ) {
if ( !$height_forced ) {
$height *= $min_width / $width;
}
$width = $min_width;
}
if ( $max_height !== "none" && $height > $max_height ) {
if ( !$width_forced ) {
$width *= $max_height / $height;
}
$height = $max_height;
}
if ( $min_height !== "none" && $height < $min_height ) {
if ( !$width_forced ) {
$width *= $min_height / $height;
}
$height = $min_height;
}
}
if (DEBUGPNG) print $width.' '.$height.';';
$style->width = $width . "pt";
$style->height = $height . "pt";
$style->min_width = "none";
$style->max_width = "none";
$style->min_height = "none";
$style->max_height = "none";
return array( $width, $width, "min" => $width, "max" => $width);
}
}

View File

@@ -0,0 +1,119 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author Fabien Ménager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Image renderer
*
* @access private
* @package dompdf
*/
class Image_Renderer extends Block_Renderer {
function render(Frame $frame) {
// Render background & borders
$style = $frame->get_style();
$cb = $frame->get_containing_block();
list($x, $y, $w, $h) = $frame->get_border_box();
$this->_set_opacity( $frame->get_opacity( $style->opacity ) );
list($tl, $tr, $br, $bl) = $style->get_computed_border_radius($w, $h);
$has_border_radius = $tl + $tr + $br + $bl > 0;
if ( $has_border_radius ) {
$this->_canvas->clipping_roundrectangle( $x, $y, $w, $h, $tl, $tr, $br, $bl );
}
if ( ($bg = $style->background_color) !== "transparent" ) {
$this->_canvas->filled_rectangle($x, $y, $w, $h, $bg);
}
if ( ($url = $style->background_image) && $url !== "none" ) {
$this->_background_image($url, $x, $y, $w, $h, $style);
}
if ( $has_border_radius ) {
$this->_canvas->clipping_end();
}
$this->_render_border($frame);
$this->_render_outline($frame);
list($x, $y) = $frame->get_padding_box();
$x += $style->length_in_pt($style->padding_left, $cb["w"]);
$y += $style->length_in_pt($style->padding_top, $cb["h"]);
$w = $style->length_in_pt($style->width, $cb["w"]);
$h = $style->length_in_pt($style->height, $cb["h"]);
if ( $has_border_radius ) {
list($wt, $wr, $wb, $wl) = array(
$style->border_top_width,
$style->border_right_width,
$style->border_bottom_width,
$style->border_left_width,
);
// we have to get the "inner" radius
if ( $tl > 0 ) {
$tl -= ($wt + $wl) / 2;
}
if ( $tr > 0 ) {
$tr -= ($wt + $wr) / 2;
}
if ( $br > 0 ) {
$br -= ($wb + $wr) / 2;
}
if ( $bl > 0 ) {
$bl -= ($wb + $wl) / 2;
}
$this->_canvas->clipping_roundrectangle( $x, $y, $w, $h, $tl, $tr, $br, $bl );
}
$src = $frame->get_image_url();
$alt = null;
if ( Image_Cache::is_broken($src) &&
$alt = $frame->get_node()->getAttribute("alt") ) {
$font = $style->font_family;
$size = $style->font_size;
$spacing = $style->word_spacing;
$this->_canvas->text($x, $y, $alt,
$font, $size,
$style->color, $spacing);
}
else {
$this->_canvas->image( $src, $x, $y, $w, $h, $style->image_resolution);
}
if ( $has_border_radius ) {
$this->_canvas->clipping_end();
}
if ( $msg = $frame->get_image_msg() ) {
$parts = preg_split("/\s*\n\s*/", $msg);
$height = 10;
$_y = $alt ? $y+$h-count($parts)*$height : $y;
foreach($parts as $i => $_part) {
$this->_canvas->text($x, $_y + $i*$height, $_part, "times", $height*0.8, array(0.5, 0.5, 0.5));
}
}
if (DEBUG_LAYOUT && DEBUG_LAYOUT_BLOCKS) {
$this->_debug_layout($frame->get_border_box(), "blue");
if (DEBUG_LAYOUT_PADDINGBOX) {
$this->_debug_layout($frame->get_padding_box(), "blue", array(0.5, 0.5));
}
}
}
}

View File

@@ -0,0 +1,74 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author Helmut Tischer <htischer@weihenstephan.org>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Decorates frames for inline layout
*
* @access private
* @package dompdf
*/
class Inline_Frame_Decorator extends Frame_Decorator {
function __construct(Frame $frame, DOMPDF $dompdf) { parent::__construct($frame, $dompdf); }
function split(Frame $frame = null, $force_pagebreak = false) {
if ( is_null($frame) ) {
$this->get_parent()->split($this, $force_pagebreak);
return;
}
if ( $frame->get_parent() !== $this )
throw new DOMPDF_Exception("Unable to split: frame is not a child of this one.");
$split = $this->copy( $this->_frame->get_node()->cloneNode() );
$this->get_parent()->insert_child_after($split, $this);
// Unset the current node's right style properties
$style = $this->_frame->get_style();
$style->margin_right = 0;
$style->padding_right = 0;
$style->border_right_width = 0;
// Unset the split node's left style properties since we don't want them
// to propagate
$style = $split->get_style();
$style->margin_left = 0;
$style->padding_left = 0;
$style->border_left_width = 0;
//On continuation of inline element on next line,
//don't repeat non-vertically repeatble background images
//See e.g. in testcase image_variants, long desriptions
if ( ($url = $style->background_image) && $url !== "none"
&& ($repeat = $style->background_repeat) && $repeat !== "repeat" && $repeat !== "repeat-y"
) {
$style->background_image = "none";
}
// Add $frame and all following siblings to the new split node
$iter = $frame;
while ($iter) {
$frame = $iter;
$iter = $iter->get_next_sibling();
$frame->reset();
$split->append_child($frame);
}
$page_breaks = array("always", "left", "right");
$frame_style = $frame->get_style();
if( $force_pagebreak ||
in_array($frame_style->page_break_before, $page_breaks) ||
in_array($frame_style->page_break_after, $page_breaks) ) {
$this->get_parent()->split($split, true);
}
}
}

Some files were not shown because too many files have changed in this diff Show More