tis-microsoft-office-365-business
16.0.17726.20206-16
Microsoft 365 Apps for business
114 downloads
See build result See VirusTotal scan
Description
- package : tis-microsoft-office-365-business
- name : Microsoft 365 Apps for business
- version : 16.0.17726.20206-16
- categories : Office
- maintainer : WAPT Team,Tranquil IT,Jimmy PELÉ
- installed_size : 3207283762
- editor : Microsoft
- licence : proprietary_restricted,wapt_enterprise
- signature_date : 2024-08-29T16:20:26.116046
- size : 2.74 Go
- locale : fr
- target_os : windows
- impacted_process : EXCEL,GROOVE,MSACCESS,MSPUB,ONENOTE,OUTLOOK,POWERPNT,WINWORD
- architecture : x64
- Homepage : https://www.office.com/
- Conflicts :
control
package : tis-microsoft-office-365-business
version : 16.0.17726.20206-16
architecture : x64
section : base
priority : optional
name : Microsoft 365 Apps for business
categories : Office
maintainer : WAPT Team,Tranquil IT,Jimmy PELÉ
description : Microsoft 365 Apps for business
depends :
conflicts : tis-microsoft-access-2016-runtime,tis-microsoft-office-2016,tis-microsoft-office-2013
maturity : PROD
locale : fr
target_os : windows
min_wapt_version : 2.3
sources : https://www.microsoft.com/download/details.aspx?id=49117
installed_size : 3207283762
impacted_process : EXCEL,GROOVE,MSACCESS,MSPUB,ONENOTE,OUTLOOK,POWERPNT,WINWORD
description_fr :
description_pl :
description_de :
description_es :
description_pt :
description_it :
description_nl :
description_ru :
audit_schedule :
editor : Microsoft
keywords : office
licence : proprietary_restricted,wapt_enterprise
homepage : https://www.office.com/
package_uuid : 2c961cda-1a6b-42e9-9ae5-3f5b6eac8fe1
valid_from :
valid_until :
forced_install_on :
changelog :
min_os_version : 10.0
max_os_version :
icon_sha256sum : 4a5a8600858fd0640c4efd2541d542498cbe2def56c3eb8ec2aba64b689cecb5
signer : Tranquil IT
signer_fingerprint: 8c5127a75392be9cc9afd0dbae1222a673072c308c14d88ab246e23832e8c6bb
signature : UaGth5MYPezJ5hb3aWw8S4asPUQ3lKKcO38Kf+Afzy8KHSAQuqZ0STkvYQOinwOf82JOSR5GjqjQ/I2ZZK/llAPjVe2c4h79VMN9iUcBnu4yNVMBWyb1muiwZKbUAdbnJsi8vLDt87+YbyY1fbFXqPSfQzeRK0O9QrC95FleLfvppE24HU0KX7t8QjI/pNsQkJVMFQHVrvl/H9Z5PNcHZiJc4o5sEBugA3gQ5hlSfinW3AmEJiC0kJ6mEwJesv0SNbN8Pg3ZS3R1X6N6us1t1OtJC1OvlNFESjp1J+xfRY9GSz6+MiIdeU+0aGO6ki6Cl0kw7r3sraIkhVA6Mu+lWA==
signature_date : 2024-08-29T16:20:26.116046
signed_attributes : package,version,architecture,section,priority,name,categories,maintainer,description,depends,conflicts,maturity,locale,target_os,min_wapt_version,sources,installed_size,impacted_process,description_fr,description_pl,description_de,description_es,description_pt,description_it,description_nl,description_ru,audit_schedule,editor,keywords,licence,homepage,package_uuid,valid_from,valid_until,forced_install_on,changelog,min_os_version,max_os_version,icon_sha256sum,signer,signer_fingerprint,signature_date,signed_attributes
Setup.py
# -*- coding: utf-8 -*-
##################################################
# This file is part of WAPT Enterprise
# All right reserved, (c) Tranquil IT Systems 2023
# For more information please refer to
# https://wapt.tranquil.it/store/licences.html
##################################################
from setuphelpers import *
import tempfile
"""
{
"key":"O365BusinessRetail - en-us",
"name":"Microsoft 365 Apps for business - en-us",
"version":"16.0.17126.20190",
"install_date":"",
"install_location":"C:\\Program Files\\Microsoft Office",
"uninstall_string":"\"C:\\Program Files\\Common Files\\Microsoft Shared\\ClickToRun\\OfficeClickToRun.exe\" scenario=install scenariosubtype=ARP sourcetype=None productstoremove=O365BusinessRetail.16_en-us_x-none culture=en-us version.16=16.0",
"publisher":"Microsoft Corporation",
"system_component":0,
"win64":true
},
{
"key":"O365BusinessRetail - en-us.proof",
"name":"Microsoft 365 Apps for business - en-us.proof",
"version":"16.0.17126.20190",
"install_date":"",
"install_location":"C:\\Program Files\\Microsoft Office",
"uninstall_string":"\"C:\\Program Files\\Common Files\\Microsoft Shared\\ClickToRun\\OfficeClickToRun.exe\" scenario=install scenariosubtype=ARP sourcetype=None productstoremove=O365BusinessRetail.16_en-us.proof_x-none culture=en-us.proof version.16=16.0",
"publisher":"Microsoft Corporation",
"system_component":0,
"win64":true
},
{
"key":"O365BusinessRetail - fr-fr",
"name":"Microsoft 365 Apps for business - fr-fr",
"version":"16.0.17126.20190",
"install_date":"",
"install_location":"C:\\Program Files\\Microsoft Office",
"uninstall_string":"\"C:\\Program Files\\Common Files\\Microsoft Shared\\ClickToRun\\OfficeClickToRun.exe\" scenario=install scenariosubtype=ARP sourcetype=None productstoremove=O365BusinessRetail.16_fr-fr_x-none culture=fr-fr version.16=16.0",
_x-none culture=fr-fr version.16=16.0",
"publisher":"Microsoft Corporation",
"system_component":0,
"win64":true
},
{
"key":"O365BusinessRetail - fr-fr.proof",
"name":"Microsoft 365 Apps for business - fr-fr.proof",
"version":"16.0.17126.20190",
"install_date":"",
"install_location":"C:\\Program Files\\Microsoft Office",
"uninstall_string":"\"C:\\Program Files\\Common Files\\Microsoft Shared\\ClickToRun\\OfficeClickToRun.exe\" scenario=install scenariosubtype=ARP sourcetype=None productstoremove=O365BusinessRetail.16_fr-fr.proof_x-none culture=fr-fr.proof version.16=16.0",
"publisher":"Microsoft Corporation",
"system_component":0,
"win64":true
}
Ressources:
https://config.office.com/deploymentsettings
https://admx.help/?Category=Office2016&Policy=office16.Office.Microsoft.Policies.Windows::L_SCLCacheOverride
https://admx.help/?Category=Office2016&Policy=excel16.Office.Microsoft.Policies.Windows::L_SaveExcelfilesas
<Product ID=""> list:
https://www.microsoft.com/microsoft-365/enterprise/microsoft-365-apps-for-enterprise-product
Microsoft 365 Apps for enterprise
O365ProPlusRetail
O365ProPlusEEANoTeamsRetail
https://www.microsoft.com/microsoft-365/business/microsoft-365-apps-for-business
Microsoft 365 Apps for business
O365BusinessRetail
O365BusinessEEANoTeamsRetail
"""
app_uninstallkey = "O365BusinessRetail - fr-fr"
def install():
# Declaring local variables
package_version = control.get_software_version()
silentflags = "/configure configuration.xml"
# uninstalling older versions of office if needed
# uninstall_mso2013_if_needed("Office15.STANDARD")
# uninstall_mso2016_if_needed("Office16.STANDARD")
# uninstall_other_office_edition(app_uninstallkey.split(" ")[0])
# Installing the software
install_exe_if_needed(
"setup.exe",
silentflags=silentflags,
timeout=1200,
accept_returncodes=[1641, 3010, 0],
key=app_uninstallkey,
min_version=package_version,
)
# TODO "setup.exe /customize configuration.xml if up-to-date"
# Adding silent uninstall command
quiet_uninstall = installed_softwares(uninstallkey=app_uninstallkey)[0]["uninstall_string"] + " DisplayLevel=False"
if "OfficeClickToRun.exe" in quiet_uninstall:
register_uninstall(
uninstallkey=app_uninstallkey,
quiet_uninstall_string=quiet_uninstall,
)
def session_setup():
print("Disabling: MSO telemetry")
# https://admx.help/?Category=Office2016&Policy=office16.Office.Microsoft.Policies.Windows::L_OfficeOSMPreventedHostApplications
registry_set(HKEY_CURRENT_USER, r"software\policies\microsoft\office\16.0\osm\preventedapplications", "wdsolution", 1)
registry_set(HKEY_CURRENT_USER, r"software\policies\microsoft\office\16.0\osm\preventedapplications", "xlsolution", 1)
registry_set(HKEY_CURRENT_USER, r"software\policies\microsoft\office\16.0\osm\preventedapplications", "pptsolution", 1)
registry_set(HKEY_CURRENT_USER, r"software\policies\microsoft\office\16.0\osm\preventedapplications", "olksolution", 1)
registry_set(HKEY_CURRENT_USER, r"software\policies\microsoft\office\16.0\osm\preventedapplications", "accesssolution", 1)
registry_set(HKEY_CURRENT_USER, r"software\policies\microsoft\office\16.0\osm\preventedapplications", "projectsolution", 1)
registry_set(HKEY_CURRENT_USER, r"software\policies\microsoft\office\16.0\osm\preventedapplications", "publishersolution", 1)
registry_set(HKEY_CURRENT_USER, r"software\policies\microsoft\office\16.0\osm\preventedapplications", "visiosolution", 1)
registry_set(HKEY_CURRENT_USER, r"software\policies\microsoft\office\16.0\osm\preventedapplications", "onenotesolution", 1)
# https://admx.help/?Category=Office2016&Policy=office16.Office.Microsoft.Policies.Windows::L_OfficeOSMPreventedSolutionTypes
registry_set(HKEY_CURRENT_USER, r"software\policies\microsoft\office\16.0\osm\preventedsolutiontypes", "documentfiles", 1)
registry_set(HKEY_CURRENT_USER, r"software\policies\microsoft\office\16.0\osm\preventedsolutiontypes", "templatefiles", 1)
registry_set(HKEY_CURRENT_USER, r"software\policies\microsoft\office\16.0\osm\preventedsolutiontypes", "comaddins", 1)
registry_set(HKEY_CURRENT_USER, r"software\policies\microsoft\office\16.0\osm\preventedsolutiontypes", "appaddins", 1)
registry_set(HKEY_CURRENT_USER, r"software\policies\microsoft\office\16.0\osm\preventedsolutiontypes", "agave", 1)
# https://admx.help/?Category=Office2016&Policy=office16.Office.Microsoft.Policies.Windows::L_EnableLogging
# registry_set(HKEY_CURRENT_USER, r"software\policies\microsoft\office\16.0\osm", "enablelogging", 0)
registry_set(HKEY_CURRENT_USER, r"software\policies\microsoft\office\16.0\osm", "enableupload", 0)
registry_set(HKEY_CURRENT_USER, r"software\policies\microsoft\office\16.0\osm", "enablefileobfuscation", 1)
# # https://admx.help/?Category=Office2016&Policy=office16.Office.Microsoft.Policies.Windows::L_Onlinecontentoptions
# registry_set(HKEY_CURRENT_USER, r"software\policies\microsoft\office\16.0\common\internet", "useonlinecontent", 0)
# # https://admx.help/?Category=Office2016&Policy=office16.Office.Microsoft.Policies.Windows::L_ServiceLevelOptions
# registry_set(HKEY_CURRENT_USER, r"software\policies\microsoft\office\16.0\common\internet", "serviceleveloptions", 0)
def uninstall():
# Uninstalling the software
for to_uninstall in installed_softwares("O365BusinessRetail"):
if ".proof" in to_uninstall["name"]:
continue
print("Removing: %s (%s)" % (to_uninstall["name"], to_uninstall["version"]))
killalltasks(ensure_list(control.impacted_process))
run(uninstall_cmd(to_uninstall["key"])[0] + " DisplayLevel=False")
wait_uninstallkey_absent(to_uninstall["key"])
def uninstall_other_office_edition(edition_uninstallkey):
# Initializing variables
silent_uninstall_file_path = makepath(tempfile.gettempdir(), "remove_other_office.xml")
uninstall_configuration_xml_content = r"""<Configuration>
<Remove All="TRUE">
</Remove>
<Display Level="none" CompletionNotice="No" SuppressModal="Yes" AcceptEula="Yes" />
<Setting Id="SETUP_REBOOT" Value="Never" />
<Setting Id="REBOOT" Value="ReallySuppress"/>
</Configuration>
"""
# Modify XML
with open(silent_uninstall_file_path, "w") as xml_file:
xml_file.write(uninstall_configuration_xml_content)
killalltasks(control.get_impacted_process_list())
# Uninstalling Office if needed
for to_uninstall in installed_softwares():
if ("OfficeClickToRun.exe" in to_uninstall["uninstall_string"]) and not (edition_uninstallkey in to_uninstall["key"]):
print("Removing: %s (%s)" % (to_uninstall["name"], to_uninstall["version"]))
app_uninstall_cmd = to_uninstall["uninstall_string"] + " DisplayLevel=False"
run(app_uninstall_cmd)
wait_uninstallkey_absent(to_uninstall["key"])
def uninstall_mso2016_if_needed(uninstallkey):
# Initializing variables
silent_uninstall_file_path = makepath(tempfile.gettempdir(), "remove_o2016.xml")
uninstall_configuration_xml_content = r"""<Configuration>
<Remove All="TRUE">
</Remove>
<Display Level="none" CompletionNotice="No" SuppressModal="Yes" AcceptEula="Yes" />
<Setting Id="SETUP_REBOOT" Value="Never" />
<Setting Id="REBOOT" Value="ReallySuppress"/>
</Configuration>
"""
# Modify XML
with open(silent_uninstall_file_path, "w") as xml_file:
xml_file.write(uninstall_configuration_xml_content)
# Uninstalling Office if needed
for to_uninstall in installed_softwares(uninstallkey=uninstallkey):
print("Removing: %s (%s)" % (to_uninstall["name"], to_uninstall["version"]))
killalltasks(control.get_impacted_process_list())
if "OfficeClickToRun.exe" in to_uninstall["uninstall_string"]:
app_uninstall_cmd = to_uninstall["uninstall_string"] + " DisplayLevel=False"
run(app_uninstall_cmd)
wait_uninstallkey_absent(to_uninstall["key"])
else:
run(to_uninstall["uninstall_string"] + ' /config "%s"' % silent_uninstall_file_path)
wait_uninstallkey_absent(to_uninstall["key"])
def uninstall_mso2013_if_needed(uninstallkey):
# Initializing variables
silent_uninstall_file_path = makepath(tempfile.gettempdir(), "remove_o2013.xml")
uninstall_configuration_xml_content = r"""<Configuration>
<Remove All="TRUE">
</Remove>
<Display Level="none" CompletionNotice="No" SuppressModal="Yes" AcceptEula="Yes" />
<Setting Id="SETUP_REBOOT" Value="Never" />
<Setting Id="REBOOT" Value="ReallySuppress"/>
</Configuration>
"""
# Modify XML
with open(silent_uninstall_file_path, "w") as xml_file:
xml_file.write(uninstall_configuration_xml_content)
# Uninstalling Office if needed
for to_uninstall in installed_softwares(uninstallkey=uninstallkey):
print("Removing: %s (%s)" % (to_uninstall["name"], to_uninstall["version"]))
killalltasks(control.get_impacted_process_list())
run(to_uninstall["uninstall_string"] + ' /config "%s"' % silent_uninstall_file_path)
wait_uninstallkey_absent(to_uninstall["key"])
update_package.py
# -*- coding: utf-8 -*-
##################################################
# This file is part of WAPT Enterprise
# All right reserved, (c) Tranquil IT Systems 2023
# For more information please refer to
# https://wapt.tranquil.it/store/licences.html
##################################################
from setuphelpers import *
import os
import sys
if "__file__" in locals():
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)))
from setupdevhelpers import *
import webbrowser
def update_package():
# Declaring local variables
package_updated = False
proxies = get_proxies()
if not proxies:
proxies = get_proxies_from_wapt_console()
extract_path = "extract"
# if not the first run it will use the last setup downloaded if you want to get an updated one you can remove setup.exe and relaunch the update package
if not isfile("setup.exe") and not params.get("running_as_luti"):
ask_message(
"You browser will open",
"Please download Office Deployment Tool by clicking the download button",
raise_error=True,
)
webbrowser.open("https://www.microsoft.com/download/details.aspx?id=49117")
ask_message("Waiting for download", "Click OK when the download is complete", 1, raise_error=True)
# Copy to pkg
latest_bin = ask_filename(
"Please provide downloaded Office Deployment Tool to copy in this package",
makepath(user_home_directory, "Downloads"),
"",
"EXE Files|*.exe",
raise_error=True,
)
# Extract Office Deployment Tool
print("Copying: " + latest_bin)
run(rf'"{latest_bin}" /extract:"{extract_path}" /quiet"')
print("Extract Office Deployment Tool")
filecopyto(".\\extract\\setup.exe", basedir)
remove_file(latest_bin)
if isdir("extract"):
remove_tree("extract")
elif params.get("running_as_luti"):
user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/88.0'
data = [ u for u in json.loads(wgets('https://www.microsoft.com/en-us/download/details.aspx?id=49117',user_agent=user_agent,proxies=proxies).split('<script>window.__DLCDetails__=',1)[1].split('</script>')[0])["dlcDetailsView"]['downloadFile'] if u['name'].endswith('.exe')][0]
download_url = data['url']
latest_bin = download_url.split("/")[-1]
wget(download_url, latest_bin, proxies=proxies)
# Extract Office Deployment Tool
print("Copying: " + latest_bin)
run(rf'"{latest_bin}" /extract:"{extract_path}" /quiet"')
print("Extract Office Deployment Tool")
filecopyto(".\\extract\\setup.exe", basedir)
remove_file(latest_bin)
if isdir("extract"):
remove_tree("extract")
# if not the first run it will use the last XML used if you want to get a new one you can remove configuration.xml and relaunch the update package
if not isfile("configuration.xml"):
ask_message(
"Your browser will open",
"Please complete your deployment settings with the online Office Customization Tool and download it by clicking Export.\nNote that you can go back anytime on the online tool to edit your configuration (by clicking Import)",
raise_error=True,
)
webbrowser.open("https://config.office.com/deploymentsettings")
ask_message("Waiting for download", "Click OK when the XML is completed and downloaded", 1, raise_error=True)
xml_configuration = ask_filename(
"Please select your XML configuration file",
basedir,
"configuration.xml",
"XML Files|*.xml",
raise_error=True,
) # makepath(user_home_directory, "Downloads")
if not isfile("configuration.xml"):
filecopyto(
xml_configuration,
basedir + "\\configuration.xml",
)
# Asking to remove the last downloaded sources
if isdir("Office"):
if not params.get("running_as_luti"):
response = ask_message(control.package, "Do you want to remove the last downloaded sources?", 35, raise_error=True)
if response == 6:
print("Removing last sources folder downloaded")
remove_tree("Office")
else:
print("Removing last sources folder downloaded")
remove_tree("Office")
else:
# Download Office with XML configuration
print("Downloading Office with XML configuration")
if windows_version() >= WindowsVersions.Windows10:
run("setup.exe /download configuration.xml", timeout=2400) # DisplayLevel=Full # not working
else:
error("MSO setup.exe can no longer be run on this OS")
# Getting version from source dir
version = glob.glob("Office/Data/**/")[0].split(os.sep)[1]
# Changing version of the package
if Version(version, 4) > Version(control.get_software_version(), 4):
print("Software version updated (from: %s to: %s)" % (control.get_software_version(), Version(version)))
package_updated = True
else:
print("Software version up-to-date (%s)" % Version(version))
control.set_software_version(version)
control.save_control_to_wapt()
# Getting Product ID and Language ID from configuration.xml parsing file
with open("configuration.xml", "r", encoding="utf8") as f:
for line in f:
if "OfficeClientEdition" in line:
architecture = line.split('"')[1]
print("OfficeClientEdition: %s" % architecture)
if architecture == "64":
architecture = "x64"
else:
architecture = "all"
if "Product ID" in line:
product_id = line.split('"')[1]
print("Product ID: %s" % product_id)
if "Language ID" in line:
language_id = line.split('"')[1]
print("Language ID: %s" % language_id)
break
if control.architecture != architecture:
control.architecture = architecture
control.save_control_to_wapt()
# Asking pkg infos if needed
if control.locale not in language_id and "-template" in control.package:
control.locale = ask_input(control.package, "You may wanna change locale to: %s" % language_id.split("-")[0], "all", raise_error=False)
print("Changing locale to: %s in WAPT\\control" % control.locale)
control.save_control_to_wapt()
complete_control_categories(control)
complete_control_name(control, control.name, params.get("running_as_luti"))
complete_control_package(control, control.package, params.get("running_as_luti"), False)
complete_control_description(control, control.description, params.get("running_as_luti"), "update_package")
# # changing uninstallkey:
# new_lines = []
# with open("setup.py", "r", encoding="utf8") as f:
# for line in f.readlines():
# if line.startswith("app_uninstallkey"):
# line = f'app_uninstallkey = "{product_id} - {language_id}"\n'
# print(f'uninstallkey in setup updated to app_uninstallkey = "{product_id} - {language_id}"\n')
# new_lines.append(line)
# with open("setup.py", "w", encoding="utf8", newline="\n") as f:
# f.writelines(new_lines)
# Validating or not update-package-sources
return package_updated
b13fe948ab9b9ceeeef29d14ef14080f2bcd023bafa43b2a73df5bec25400fe3 : setup.py
: __pycache__
c1b5b245355d4565acdce983e48fd576eb9706ec5ffcc3cfac65b6f5fc2305ae : configuration.xml
9e3b1225163144ab81587041643fb8a124b261600adfc0c23d422c29f220d10e : Office/Data/v64.cab
215b96d9312f640a798064803713cc16007a7ad5ff7706bf9b51170e33e1a7ce : Office/Data/16.0.17726.20206/a640_exp.cab
095e85103826244b773d194ea3c04542ee9999449263b05e107a2adf15975ad9 : Office/Data/16.0.17726.20206/stream.x64.fr-fr.dat
62024ae4185cc341ee41c3567de78faa94396019f711f20f56e002bbbf5ccbcd : Office/Data/16.0.17726.20206/i641036.cab
c3cbadeaf9d1c024798e8e62b6bcb5d1c1d3c96c24bd59b86fc1c21ab7c3fc10 : Office/Data/16.0.17726.20206/s641036.cab
c0f82b23a920f563f7decf9710f3fb83c3aacdb5a9c34f8c54cd6abd2b5ce89c : Office/Data/16.0.17726.20206/stream.x64.x-none.dat.cat
2d96e377ad832ae49d816decc69052063df0a94c467a91663ac06a1890e2ba3e : Office/Data/16.0.17726.20206/i640.cab.cat
012acf7cd83d0bfe448fd990402d795b55deb8e871e1e50246ab8aab8234338f : Office/Data/16.0.17726.20206/i640.cab
74a51383f8fe96e3e35eb32e57214a2b9744b5f8b9bddfa0443560b684a6e695 : Office/Data/16.0.17726.20206/stream.x64.x-none.dat
22ee4efef5710ebbc4ecb9ea079561b118dae89cc04ed3f091819708431d27f9 : Office/Data/16.0.17726.20206/stream.x64.fr-fr.dat.cat
cdd6df5c2d6e4bd4f34dae1bab59e94578ec817fd5c3fac55c87012ac0156e65 : Office/Data/16.0.17726.20206/s640.cab
9e3b1225163144ab81587041643fb8a124b261600adfc0c23d422c29f220d10e : Office/Data/v64_16.0.17726.20206.cab
: Office
74fc6da86dc21dc5b78aecff97f8914000150395bc76eacd420b842fe0f8ddd9 : update_package.py
4a5a8600858fd0640c4efd2541d542498cbe2def56c3eb8ec2aba64b689cecb5 : WAPT/icon.png
a5a97261381e1d0ad46ee15916abec9c2631d0201f5cc50ceb0197a165a0bbbf : WAPT/certificate.crt
7fd5bdd763ff68c4fd73059df1248123f6ded4ee64bfe6c2926ccf5d1d95f341 : luti.json
335932e68d7a8bfa4fa00bbcdea09256ed6ecab2bedd640e2580b0b43e3b7744 : setup.exe
7dff844daea24cdd577a2beecd42fa7d8e7c0c0cd9995cb2e58588da42c3ad7b : WAPT/control